From 12686ecb83d9df54c08ad31b2c9ec3569961827b Mon Sep 17 00:00:00 2001 From: jan madsen Date: Thu, 1 Nov 2018 17:55:55 +0000 Subject: [PATCH 001/289] Translated using Weblate (Danish) Currently translated at 79.4% (274 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/ --- app/src/main/res/values-da/strings.xml | 158 ++++++++++++++----------- 1 file changed, 90 insertions(+), 68 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index a8607c372..d805083b9 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -20,27 +20,27 @@ Danish translation by Frederik Svarre (fsvarre@gmail.com) --> - Tilbagemelding: - Hjemmeside: - KeePass DX er en Android implementering af KeePass password manager. + Tilbagemelding + Hjemmeside + Android implementering af KeePass password manager Accepter Tilføj post Tilføj gruppe Krypteringsalgoritme Timeout - Tid før databasen låses, når programmet er inaktiv + Inaktivitet før programmet bliver er låst App Indstillinger Vis ikke igen Parenteser - Filhåndtering kræver at OpenIntents File Manager er installeret, klik nedenfor for at gøre dette. På grund af nogle særheder i filhåndtering, fungerer det muligvis ikke korrekt i første omgang. + Installer OpenIntents Fil Manager for at gennemse filer Annuller Udklipsholder ryddet Udklipsfejl - Nogle Samsung Android enheder har en fejl i implementeringen af udklipsholderen, som får kopiering fra apps til at mislykkes. Flere oplysninger: - Rydningen af udklipsholderen fejlede + Nogle Samsung Android-telefoner, vil ikke lade programmer bruge udklipsholderen. + Kunne ikke rydde udklipsholderen Udklipsholder timeout - Tid før udklipsholderen bliver ryddet efter kopiering af brugernavn eller adgangskode + Varighed af opbevaring i udklipsholderen Vælg for at kopiere %1$s til udklipsholder Opretter databasenøgle… Database @@ -48,10 +48,10 @@ Brug som standarddatabase Cifre KeePass DX \u00A9 %1$d Kunzisoft kommer med ABSOLUT INGEN GARANTI; Det er fri software, og kan videredistribueres under betingelserne i GPL version 3 eller nyere. - Vælg en eksisterende database + Åbn en eksisterende database Senest åbnet Annuller - Kommentarer + Noter Bekræft adgangskode Oprettet Udløber @@ -64,27 +64,27 @@ URL Brugernavn ARCFOUR stream cipher er ikke understøttet. - KeePass DX ikke kan håndtere denne URI. + Kunne ikke håndtere URI i KeePass DX. Kunne ikke oprette rodbiblioteket. Filen eksisterer allerede. Kunne ikke åbne linket. - Et filnavn er påkrævet. + Indtast et filnavn. Kunne ikke oprette fil: - Ugyldig database eller ukendt hovednøgle. - Ugyldig sti. - Et navn er påkrævet. - En nøglefil er påkrævet. - Enheden løb tør for hukommelse under parsing af databasen. - Der skal vælges mindst én kode for kodeordsgenerering + Kunne ikke læse databasen. + Sørg for, at stien er korrekt. + Indtast et navn. + Vælg en nøglefil. + Ikke nok hukommelse til at indlæse hele databasen. + Der skal vælges mindst én kode for kodeordsgenerering. Adgangskoderne er ikke ens. - \"Runder\" skal være en talværdi. - \"Runder\" er for stor. Sættes til 2147483648. + \"Transformationsrunder\" skal være en talværdi. + \"Transformation Runder\" er for stor. Sættes til 2147483648. Hver streng skal have et feltnavn. - En titel er påkrævet. - Angiv et positivt heltal i feltet \"Længde\" + Tilføj en titel. + Angiv et positivt heltal i feltet \"Længde\". Feltnavn Feltværdi - Filen blev ikke fundet. + Kunne ikke finde filen. Filhåndtering Generer adgangskode bekræft adgangskode @@ -97,8 +97,8 @@ Installer fra Google Play Installer fra F-Droid Ugyldig adgangskode eller nøglefil. - Ugyldig algoritme. - Databaseformatet blev ikke genkendt. + Forkert algoritme. + Kunne ikke genkende databaseformat. Nøglefil eksisterer ikke. Nøglefilen er tom. Længde @@ -106,10 +106,10 @@ Tekststørrelse i elementliste Indlæser database… Små bogstaver - Masker adgangskode - Skjul adgangskoder som standard + Skjul adgangskoder + Masker adgangskoder (***) som standard Om - Skift masternøgle + Skift hovednøgle Indstillinger Database indstillinger Slet @@ -124,25 +124,25 @@ Bindestreg Aldrig Ingen søgeresultater - Kan ikke håndtere denne URL. - Seneste databaser: + Installer en web-browser til at åbne URL. + Seneste databaser Gennemsøg ikke backup poster - Udelad \"Backup\" - gruppen fra søgeresultaterne (gælder kun for .kdb filer) + Udelad \"Backup\" - gruppe fra søgeresultaterne (gælder kun for .kdb filer) Opretter ny database… Arbejder… Beskyttelse - KeePass DX har ikke rettigheder til at skrive til databasen, databasen vil blive åbnet som skrivebeskyttet. + KeePass DX behøver skrivetilladelse for at ændre i databasen. Startende med Android KitKat, tillader nogle enheder ikke længere apps at skrive til SD-kortet. Seneste filhistorik - Husk senest anvendte filnavne - Husker placeringen af nøglefiler + Husk de seneste filnavne + Husker placeringen af databasernøglefiler Gem nøglefil Fjern Rijndael (AES) Rod - Transformér \"Runder\" + Transformationsrunder Højere antal krypteringsrunder giver øget beskyttelse imod brute-force angreb, men kan påvirke læsnings- og skrivehastigheden betydentligt. - runder + Transformationsrunder Gemmer database… Mellemrum Søg @@ -184,11 +184,11 @@ Udvidet ASCII Tillad Stryg for at at rydde udklipsholder nu - Database kan ikke indlæses - Ude af stand til at indlæse nøglen, prøv at reducere den hukommelse, der bruges af KDF. - Tjenesten AutoFyld kan ikke aktiveres. + Databasen kunne ikke indlæses. + Kunne ikke indlæse nøglen. Prøv at reducere KDF \"hukommelsesforbrug\". + Kunne ikke aktivere autofyld tjenesten. Kan ikke flytte en gruppe til sig selv. - Filen blev ikke fundet. Prøv at genåbne fra indholdsudbyderen. + Kunne ikke finde filen. Prøv at åbne den fra filhåndtering. Vis brugernavne Vis brugernavne i postlister Kopi af %1$s @@ -197,12 +197,12 @@ Flyt Indsæt Annuller - Fjern fingeraftryksnøgle + Slet gemt fingeraftryk Skrivebeskyttet - Læs og skriv + Modificerbar Skrivebeskyttet Algoritme til kryptering af hele databasen. (adgangskoder, brugernavne, noter og alle data i databasen er krypteret med den valgte algoritme) - For at generere nøglen til krypteringsalgoritmen, omdannes den komprimerede hovednøgle (SHA-256) ved hjælp af et tilfældigt saltet nøgleafledning funktion. + For at generere nøglen til krypteringsalgoritmen, omdannes hovednøglen ved hjælp af en tilfældigt saltet nøgleafledningsfunktion. Hukommelsesforbrug Hukommelse (i binær byte), som anvendes af nøgleafledningsfunktion. Parallelitet @@ -213,9 +213,9 @@ Papirkurv nederst Titel Brugernavn - Oprettelsestidspunkt - Tidspunkt for seneste ændring - Senest åbnet + Oprettelse + Ændring + Adgang Brug Android Storage Access (SAF) som filhåndtering (KitKat og senere) Storage Access Framework Advarsel @@ -223,10 +223,10 @@ Brug en tom streng som adgangskode? Bekræft ingen brug af en krypteringsnøgle? Fingeraftryk er understøttet, men ikke konfigureret for denne enhed - Venter på fingeraftryk + Fingeraftryks-scanning Krypteret adgangskode er gemt Ugyldig fingeraftryksnøgle. Gendan adgangskode. - Fingeraftryk ikke genkendt + Kunne ikke genkende fingeraftryk Problem med fingeraftryk: %1$s Brug fingeraftryk til at gemme adgangskoden Ingen adgangskode er endnu gemt i databasen @@ -257,7 +257,7 @@ Scan fingeraftryk, når afkrydsningsfeltet for adgangskoden ikke er markeret, for at åbne databasen Brug Fingeraftryk - Scanner for fingeraftryk + Fingeraftryks-scanning Aktiver åbning af databasen med fingeraftryk Slet krypteringsnøgler Slet alle krypteringsnøgler, der er relateret til fingeraftryk @@ -268,30 +268,30 @@ Filnavn Sti Tildel en hovednøgle - Oprette en KeePass fil + Opret en ny database Bytes Filsti Se den fulde filsti - Brug Papirkurv - Flyt en gruppe eller post til \"Papirkurven\" før den slettes - KeePass DX skal have tilladelse til ekstern lagring for at skrive til en database - KeePass DX skal have tilladelse til ekstern lagring for at læse en URI, der ikke leveres af en indholdsleverandør + Brug papirkurven + Flyt grupper og poster til \"Papirkurven\" før den slettes + KeePass DX skal have tilladelse til ekstern lagring for at skrive til en database. + KeePass DX skal have tilladelse til ekstern lagring for at læse en URI, der ikke leveres af en indholdsleverandør. Tilladelse til ekstern lager nægtet - Kan ikke udføre handlingen uden ekstern lager tilladelse + Kan ikke udføre handlingen uden ekstern lager tilladelse. Feltskrifttype Skift skrifttypen, der anvendes i felter, for at forbedre tegnsynlighed - Åbn automatisk valgte fil - Åbn filen automatisk efter valg i filhåndtering + Åbn filer ved at vælge + Åbn filer automatisk efter valg i filhåndtering Kopi af adgangskode Tillader kopiering af adgangskoden og beskyttede felter til udklipsholder ADVARSEL: Udklipsholder deles af alle apps. Hvis følsomme data er kopieret, kan andet software gendanne den. ADVARSEL: Deaktivering af funktionen kan resultere i en manglende evne til at åbne eller gemme databaser - Link til KDBX-filen der skal åbnes + Link til database-filen der skal åbnes Databasenavn Database beskrivelse Databaseversion - Tekst udseende - App udseende + Tekst + Program Øvrige Tastatur @@ -299,7 +299,7 @@ Aktiver et brugerdefineret tastatur, der nemt udfylder adgangskoder og alle identitetsfelter Magikeyboard indstillinger Hvordan konfigureres tastaturet til sikker formularudfyldning? - Aktiver Magikeyboard i enhedens indstillinger. + Aktiver Magikeyboard\" i enhedens indstillinger. \"Indstillinger\" → \"Sprog & input\" → \"Aktuelt tastatur\" og vælg et. eller (\"Indstillinger\" → \"Sprog & input\" → \"Virtuelt tastatur\" og vælg et.) Vælg Magikeyboard, når der er brug for at udfylde en formular. @@ -310,7 +310,7 @@ Gå tilbage til det primære tastatur. Tillad ingen adgangskode - Aktiver knappen Åbn, hvis der ikke er valgt en adgangskodeidentifikation + Aktiver knappen \"Åbn\", hvis der ikke er valgt en adgangskodeidentifikation Skrivebeskyttet Som standard åbne databaser skrivebeskyttet @@ -325,18 +325,18 @@ Har allerede brugt en KeePass manager. Bare åbn KDBX filen fra filhåndtering. Et link til filen er tilstrækkelig Databasen kan også åbnes med en fysisk forbindelse (med file:// og content:// for eksempel). - Tilføj nye elementer til databasen + Tilføj elementer til databasen Tilføje poster til at styre digitale identiteter. \n \nTilføje grupper (svarende til mapper) for at organisere indtastninger og database. - Nemt at søge i poster + Søg i poster Søg efter poster efter titel, brugernavn, eller på andre felter for nemt at hente adgangskoder. Lås databasen op med fingeraftryk Lav en forbindelsen mellem kodeord og fingeraftryk for nemt at låse databasen op. Rediger posten Rediger post med brugerdefinerede felter, henvisninger til pooldata kan tilføjes mellem felter i forskellige poster. Opret en stærk adgangskode - Generer en stærk kodeord til at forbinde elementet, definer det i henhold til kriteriet for formularen og glem ikke et sikkert kodeord og husk på det. + Generer en stærk kodeord til at forbinde elementet, definer det i henhold til kriteriet for formularen og glem ikke et sikkert kodeord. Tilføj brugerdefinerede felter Registrer et felt, der ikke allerede eksisterer, indtast blot i det den nye, som også kan beskyttes visuelt. Lås databasen op @@ -355,7 +355,7 @@ Sorter elementer Sorter poster og grupper i henhold til specifikke parametre. Deltag - Deltag for at hjælpe med at øge stabiliteten, sikkerheden og ved at tilføje flere funktioner. + Bidrag til at øge stabiliteten, sikkerheden og med at tilføje flere funktioner. I modsætning til andre programmer til adgangskodeadministration er denne annoncefri , copyleft fri software, og indsamler ikke personlige data på servere, selv i den gratis version. Ved at købe pro-versionen, er der adgang til visuel funktionen, og det vil især hjælpe gennemførelsen af lokale projekter. @@ -380,7 +380,29 @@ Vælg et tema Ændre tema ved at ændre farver - Vælg en ikonpakke + Ikonpakke Skift ikonpakke - +Magikeyboard + Magikeyboard (KeePass DX) + Magikeyboard indstillinger + + Post + + Timeout + Meddelsesinformation + 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 indlæg, når meddelse lukkes + Udseende + + Tastaturtema + + Taster + Vibrer ved tastetryk + Lyd ved tastetryk + From 293b7e3a05080cd5f55d45f5698837e2048ee4ee Mon Sep 17 00:00:00 2001 From: Heimen Stoffels Date: Thu, 1 Nov 2018 16:55:01 +0000 Subject: [PATCH 002/289] Translated using Weblate (Dutch) Currently translated at 77.9% (269 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/ --- app/src/main/res/values-nl/strings.xml | 101 +++++++++++++------------ 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 81a8174ab..ef4819e02 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -20,23 +20,23 @@ Dutch translation by Erik Devriendt, corrected by Erik Jan Meijer --> - Feedback: - Website: - KeePass DX is een Android-implementatie van de KeePass-wachtwoordbeheerder. + Feedback + Website + Android-implementatie van de KeePass-wachtwoordbeheerder Accepteren Item toevoegen Groep toevoegen Algoritme App-time-out - Tijd tot het vergrendelen van de databank bij inactiviteit + Tijd tot vergrendelen bij inactiviteit App App-instellingen Haakjes - Een bestand opzoeken kan alleen met de Open Intents File Manager. Druk hieronder om deze app te installeren. Let op: door gebreken in deze bestandsbeheerder kan het opzoeken van een bestand de eerste keer mislukken. + Zoek een bestand op door het installeren van de OpenIntents File Manager Annuleren Klembord gewist Klembordtime-out - Tijd tussen het kopiëren van gebruikersnaam of wachtwoord en het wissen van het klembord + Tijd van opslag op het klembord Selecteer om %1$s naar klembord te kopiëren Bezig met creëren van databanksleutel… Databank @@ -44,7 +44,7 @@ Gebruiken als standaarddatabank Getallen KeePass DX \u00A9 %1$d Kunzisoft biedt GEEN ENKELE GARANTIE. Dit is vrije software, dus je mag deze software verspreiden onder de voorwaarden van de GPL versie 3 of recenter. - Kies een bestaande databank + Bestaande databank openen Laatst geopend Annuleren Opmerkingen @@ -63,19 +63,19 @@ Kan bovenliggende map niet creëren. Dit bestand bestaat al. Kan link niet openen. - Bestandsnaam vereist. + Voer een bestandsnaam in. Kan bestand niet creëren: - Ongeldige databank of niet-herkende hoofdsleutel. - Ongeldig pad. - Naam vereist. - Sleutelbestand vereist. - Onvoldoende vrij geheugen om de databank uit te lezen. - Je moet minimaal één soort wachtwoordgenerering kiezen + Kan databank niet uitlezen. + Zorg ervoor dat het pad juist is. + Voer een naam in. + Kies een sleutelbestand. + Onvoldoende vrij geheugen om de gehele databank te laden. + Je moet minimaal één soort wachtwoordgenerering kiezen. De wachtwoorden komen niet overeen. \"Cycli-waarde\" moet een getal zijn. \"Cycli-waarde\" te groot. Wordt ingesteld op 2147483648. - Titel vereist. - Voer een positief geheel getal in in het veld \"Lengte\" + Voeg een titel toe. + Voer een positief geheel getal in in het veld \"Lengte\". Bestand niet gevonden. Bestandsverkenner Wachtwoord genereren @@ -89,14 +89,14 @@ Installeren via Play Store Installeren via F-Droid Ongeldig wachtwoord of sleutelbestand. - Databankformaat niet herkend. + Databankformaat kan niet worden herkend. Lengte Grootte van itemlijst Tekstgrootte van itemlijst Bezig met laden van databank… Kleine letters - Wachtwoord afschermen - Wachtwoorden standaard verbergen + Wachtwoord verbergen + Wachtwoorden standaard afschermen (***) Over Hoofdsleutel wijzigen Instellingen @@ -113,13 +113,13 @@ Minus Nooit Geen zoekresultaten - Geen afhandeling voor deze URL mogelijk. - Recente databanken: + Installeer een webbrowser om deze URL te openen. + Recente databanken Back-upitems niet doorzoeken - \'Back-up\'-groep verbergen uit zoekresultaten (alleen van toepassing op .kdb-bestanden) + \"Back-up\"-groep verbergen uit zoekresultaten (alleen van toepassing op .kdb-bestanden) Bezig met creëren van nieuwe databank… Bezig met verwerken… - Locatie van sleutelbestanden onthouden + Locatie van databank-sleutelbestanden onthouden Sleutelbestand opslaan Verwijderen Rijndael (AES) @@ -130,15 +130,15 @@ Bezig met opslaan van databank… Ruimte Zoeken - DB-sorteervolgorde + Natuurlijke databank Speciaal Zoeken Twofish Onderstrepen Niet-ondersteunde databankversie. Hoofdletters - Je SD-kaart is momenteel alleen-lezen. Wijzigingen aan de databank kunnen niet worden opgeslagen. - Je sd-card is momenteel niet aangekoppeld. Je kunt geen databanken laden of creëren. + Machtig schrijftoegang om de databankwijzigingen op ge slaan. + Koppel de SD-kaart aan om een databank te creëren of laden. Versie %1$s Geef het wachtwoord en/of sleutelbestand op om je databank te ontgrendelen. \n @@ -168,18 +168,18 @@ Uitgebreide ASCII Toestaan Klembordfout - Sommige Android-telefoons van Samsung bevatten een fout in de klembordimplementatie, wat leidt tot kopieerfouten. Meer informatie: + Op sommige Samsung-telefoons hebben apps geen toegang tot het klembord. Wissen van klembord mislukt Veeg om klembord nu te wissen - Inhoud van item niet gevonden. - Databank kan niet worden geladen - De sleutel kan niet worden geladen. Probeer om het geheugengebruik van KDF te verminderen. + Geen iteminhoud gevonden. + Je databank kan niet worden geladen. + De sleutel kan niet worden geladen. Probeer om het \"geheugengebruik\" van KDF te verminderen. Elke zin moet een veldnaam bevatten. De dienst automatisch aanvullen kan niet worden ingeschakeld. Een groep kan niet naar zichzelf worden verplaatst. Veldnaam Veldwaarde - Bestand niet gevonden. Probeer opnieuw te openen van je inhoudsdienst. + Bestand niet gevonden. Probeer opnieuw te openen via je bestandsbeheerder. Ongeldig algoritme. Er bestaat geen sleutelbestand. Het sleutelbestand is leeg. @@ -191,17 +191,17 @@ Verplaatsen Plakken Annuleren - Vingerafdruksleutel verwijderen + Opgeslagen vingerafdruk verwijderen Alleen-lezen Lezen en schrijven Beveiliging Alleen-lezen - KeePass DX is niet gemachtigd om items weg te schrijven naar je databanklocatie; het bestand wordt geopend als alleen-lezen. + KeePass DX moet worden gemachtigd om je databank te kunnen aanpassen. Sinds Android KitKat is het op sommige apparaten niet langer toegestaan om apps weg te schrijven naar de SD-kaart. Recente bestandgeschiedenis Recent gebruikte bestandsnamen onthouden - Het algoritme dat moet worden gebruikt om de gehele databank te versleutelen. (Wachtwoorden, gebruikersnamen, notities en alle databankgegevens worden hiermee versleuteld) - Om de sleutel voor het algoritme te kunnen genereren, wordt de ingepakte hoofdsleutel (SHA-256) getransformeerd middels een willekeurige afleidingsfunctie. + Het algoritme dat moet worden gebruikt om de gehele databank te versleutelen. + Om de sleutel voor het algoritme te kunnen genereren, wordt de hoofdsleutel getransformeerd middels een willekeurige afleidingsfunctie. Geheugengebruik Het geheugen (in binaire bytes) dat de afleidingsfunctie mag gebruiken. Parallellen @@ -212,24 +212,24 @@ Prullenbak onderaan plaatsen Titel Gebruikersnaam - Gecreëerd om - Laatst aangepast om - Laatst geopend om + Gecreëerd op + Aangepast om + Geopend om Zoekresultaten Android-opslagtoegang-framework (SAF) gebruiken voor bestandsverkenning (KitKat en hoger) Opslagtoegang-framework Waarschuwing - Je wachtwoord mag letters bevatten die niet ondersteund worden door de Latin-1-tekenset die gebruikt wordt door .kdb-bestanden. Ze worden allemaal omgezet naar dezelfde letter, dus het wordt aanbevolen om je wachtwoord te wijzigen. - Weet je zeker dat je je wachtwoord wilt leeglaten? + Vermijd letters bevatten die niet ondersteund worden door de Latin-1-tekenset van .kdb-bestanden; ze worden allemaal omgezet naar dezelfde letter. + Weet je zeker dat je geen wachtwoordontgrendelbescherming wilt\? Weet je zeker dat je geen sleutel wilt koppelen aan je versleuteling? - Vingerafdruk wordt ondersteund, maar is niet ingesteld op dit apparaat - Bezig met zoeken naar vingerafdrukken + Vingerafdruk wordt ondersteund, maar is niet ingesteld. + Vingerafdrukscanning Versleuteld wachtwoord is opgeslagen - Ongeldige vingerafdruk. Herstel je wachtwoord. + Kan vingerafdruksleutel niet lezen; herstel je wachtwoord. Vingerafdruk niet herkend Vingerafdrukprobleem: %1$s Vingerafdruk gebruiken om dit wachtwoord op te slaan - Nog geen wachtwoord opgeslagen voor deze databank + Deze databank heeft nog geen wachtwoord. Geschiedenis Uiterlijk Algemeen @@ -238,22 +238,22 @@ Inloggen met KeePass DX Standaard aanvuldienst instellen Schakel de dienst in om formulieren in andere apps snel in te vullen - Wachtwoordgrootte + Gegenereerde wachtwoordgrootte Standaardgrootte instellen van gegenereerd wachtwoord Wachtwoordtekens Standaard wachtwoordtekens instellen Klembord Klembordmeldingen Schakel klembordmeldingen in om itemvelden te kopiëren - Als je apparaat niet automatisch items van het klembord kan verwijderen, verwijder het gekopieerde item dan handmatig van het klembord. + Als automatisch wissen van het klembord mislukt, doe dit dan handmatig. Vergrendelen Schermvergrendeling Databank vergrendelen als het scherm uitgaat Hoe stel ik mijn vingerafdruk in voor snelle ontgrendeling? Stel je persoonlijke vingerafdruk voor je apparaat in via \"Instellingen\" → \"Beveiliging\" → \"Vingerafdruk\" - Typ je wachtwoord in KeePass DX - Scan je vingerafdruk om je hoofdwachtwoord veilig op te slaan + Voer het databankwachtwoord in + Scan je vingerafdruk om je databankwachtwoord veilig op te slaan. Scan je vingerafdruk om de databank te ontgrendelen als er geen vinkje staat bij wachtwoord Gebruik Vingerafdruk @@ -268,7 +268,7 @@ Bestandsnaam Pad Hoofdsleutel toewijzen - KeePass-bestand creëren + Nieuwe databank creëren bytes Bestandspad Volledig bestandspad tonen @@ -383,4 +383,5 @@ Kies een pictogramthema Wijzig het pictogramthema van de app - +Bouw %1$s + From 4b760c6361ae775c1595f769faa418610d3cf76c Mon Sep 17 00:00:00 2001 From: Kunzisoft Date: Thu, 1 Nov 2018 17:23:53 +0000 Subject: [PATCH 003/289] Translated using Weblate (French) Currently translated at 50.1% (173 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 51b782e03..72c84f25c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -18,15 +18,15 @@ along with KeePass DX. If not, see . --> - Signaler une anomalie : - Site web : - KeePass DX est une implémentation sur Android du gestionnaire de mots de passe KeePass. + Signaler une anomalie + Site web + Implémentation Android du gestionnaire de mots de passe KeePass. Accepter Ajouter une entrée Ajouter un groupe Ajouter une chaîne Chiffrement - Algorithme de Chiffrement + Algorithme de chiffrement Fonction de dérivation de clé Délai d\'expiration de l\'application Temps avant le verrouillage de la base de données lorsque l\'application est inactive From 89996f09f199677ba6828b7058d2b97f71f034b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=2E=20R=C3=BCdinger?= Date: Fri, 2 Nov 2018 14:31:45 +0000 Subject: [PATCH 004/289] Translated using Weblate (German) Currently translated at 90.4% (312 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 185 ++++++++++++++----------- 1 file changed, 105 insertions(+), 80 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 150595ce0..17ce29cc6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -22,39 +22,39 @@ Translations from David Ramiro --> - Rückmeldung: - Webseite: - KeePass DX ist eine Implementierung des KeePass Passwortmanagers für die Android-Plattform. + Rückmeldung + Webseite + Android-Implementierung des Passwortmanagers KeePass Akzeptieren Eintrag hinzufügen Gruppe hinzufügen Text hinzufügen Verschlüsselungsalgorithmus App-Sperre - Bei inaktiver App wird die Datenbank nach Ablauf der eingestellten Zeit automatisch gesperrt + Dauer der Inaktivität bis die App gesperrt wird App Einstellungen Nicht mehr anzeigen Klammern - Um diese Funktion nutzen zu können, benötigen Sie den OI File Manager, den Sie über Google Play bzw. direkt von der Entwicklerwebseite herunterladen können. Aufgrund einiger Eigenheiten des Dateimanagers, kann es bei der ersten Benutzung zu Einschränkungen kommen. + Durchsuchen Sie Ihre Dateien, indem Sie den OpenIntents File Manager installieren Abbrechen Zwischenablage geleert Zwischenablagefehler - Die Implementierung der Zwischenablagefunktion in einigen Samsung Android-Smartphones ist offenbar fehlerhaft, wodurch das Kopieren von Daten aus der Anwendung heraus fehlschlägt. Weitere Informationen dazu finden Sie unter: + Einige Samsung Android-Smartphones lassen keine Nutzung der Zwischenablage durch Apps zu. Leeren der Zwischenablage fehlgeschlagen Zwischenablagesperre - Zeit nach dem Kopieren eines Benutzernamens oder Passworts, bevor die Zwischenablage geleert wird + Dauer der Speicherung in der Zwischenablage %1$s in die Zwischenablage kopieren Datenbank-Schlüsseldatei erzeugen\u2026 Datenbank - Entschlüsselung der Datenbankinhalte\u2026 + Entschlüsselung der Datenbankinhalte … Als Standard-Datenbank benutzen Zahlen KeePass DX \u00A9 %1$d Kunzisoft. Alle Rechte vorbehalten. Die Nutzung der Software erfolgt auf eigene Verantwortung und ohne jegliche Garantie. Die Applikation ist kostenlos und wird unter den Bedingungen der GNU GPL Version 3 (oder später) verbreitet und lizenziert. - Dateinamen der Datenbank eingeben + Vorhandene Datenbank öffnen Letzter Zugriff Abbrechen - Kommentare + Notizen Passwort wiederholen Erstelldatum Ablaufdatum @@ -71,24 +71,24 @@ Verzeichnis konnte nicht erstellt werden. Die Datei existiert bereits. Fehler beim Öffnen der Verknüpfung. - Ein Dateiname wird benötigt. + Dateinamen eingeben. Konnte Datei nicht erstellen: - Ungültige Datenbank. - Ungültiger Pfad. - Ein Name wird benötigt. - Ein Passwort oder eine Schlüsseldatei wird benötigt. - Der Speicher Ihres Smartphones reicht nicht aus, um die Datenbank zu analysieren. - Mindestens eine Art der Passwortgenerierung muss ausgewählt werden + Datenbank nicht lesbar. + Stellen Sie sicher, dass der Pfad korrekt ist. + Namen eingeben. + Schlüsseldatei wählen. + Zu wenig Speicherplatz, um die ganze Datenbank zu laden. + Mindestens eine Art der Passwortgenerierung muss ausgewählt werden. Die Passwörter stimmen nicht überein. Die Anzahl der Wiederholungen muss eine Zahl sein. Die maximale Wiederholungsanzahl ist 2147483648. Für jede Zeichenfolge ist ein Feldname notwendig. - Ein Titel wird benötigt. - Geben Sie die erforderliche Länge als positive, ganze Zahl in das Feld ein + Titel hinzufügen. + Geben Sie die erforderliche Länge als positive, ganze Zahl in das Feld ein. Feldname Feldwert Datei nicht gefunden. - Datei nicht gefunden. Versuchen Sie, es von Ihrem Dienstanbieter erneut zu öffnen. + Datei nicht gefunden. Versuchen Sie, sie über Ihren Dateimanger zu öffnen. Dateimanager Passwort generieren Passwort wiederholen @@ -101,8 +101,8 @@ Google Play F-Droid Passwort oder Schlüsseldatei ist falsch. - Ungültiger Algorithmus. - Datenbankformat wurde nicht erkannt. + Falscher Algorithmus. + Datenbankformat nicht erkannt. Es existiert keine Schlüsseldatei. Die Schlüsseldatei ist leer. Länge @@ -111,7 +111,7 @@ Datenbank wird geladen\u2026 Kleinbuchstaben Passwort verstecken - Passwörter standardmäßig verstecken + Passwörter standardmäßig mit (***) maskieren Über Master-Schlüssel ändern Einstellungen @@ -128,15 +128,15 @@ Bindestrich Nie Keine Suchergebnisse - Kein Handler zum Öffnen der URL vorhanden. - Zuletzt geöffnete Datenbanken: + Installieren Sie einen Web-Browser, um diese URL zu öffnen. + Zuletzt geöffnete Datenbanken Papierkorb/Sicherungen nicht durchsuchen - Papierkorb und Sicherungseinträge werden bei der Suche nicht berücksichtigt (nur bei .kdb Dateien) + Sicherungseinträge werden bei der Suche nicht berücksichtigt (nur bei .kdb Dateien) Neue Datenbank anlegen\u2026 Ausführen\u2026 Sicherheit - Schreibschutz - KeePass DX hat keine Schreibrechte für den Speicherort Ihrer Datenbank. Daher wird sie schreibgeschützt geöffnet. + Schreibgeschutzt + KeePass DX benötigt Schreibrechte, um etwas an Ihrer Datenbank zu ändern. Ab Android KitKat haben auf einigen Geräten Apps keine Schreibrechte mehr für die SD-Karte. Zuletzt verwendete Datenbanken Zuletzt verwendete Datenbanken merken @@ -146,7 +146,7 @@ Rijndael (AES) Start Schlüsseltransformationen - Je höher die Anzahl der Schlüsseltransformationen, desto besser ist der Schutz gegen Wörterbuch- oder Brute-Force-Angriffe. Allerdings dauert dann auch das Laden und Speichern der Datenbank entsprechend länger. + Zusätzliche Schlüsseltransformationen bieten einen besseren Schutz gegen Wörterbuch- oder Brute-Force-Angriffe. Allerdings dauert dann auch das Laden und Speichern der Datenbank entsprechend länger. Schlüsseltransformationen Speichere Datenbank\u2026 Leerzeichen @@ -162,9 +162,9 @@ Storage Access Framework (SAF) als Dateimanager verwenden (Android KitKat und später) Speicherzugriff-Framework Warnung - Ihr Passwort enthält Zeichen, die nicht Teil des Latin-1-Zeichensatzes sind, der im .kdb Format verwendet wird. Da diese Zeichen in ein und dasselbe Zeichen umgewandelt werden, wird empfohlen, das Passwort zu ändern, um dessen Sicherheit zu erhöhen. - Die SD-Karte ist schreibgeschützt. Etwaige Änderungen in den Datensätzen können daher nicht in der Datenbank gespeichert werden. - Keine SD-Karte vorhanden oder derzeit nicht im Gerät eingebunden. Daher kann weder eine Datenbank geöffnet, noch erstellt werden. + Vermeiden Sie Passwortzeichen, die nicht Teil des Latin-1-Zeichensatzes sind, in .kbd-Dateien, da sie alle in ein und dasselbe Zeichen umgewandelt werden. + Gewähren Sie Schreibzugriff auf die SD-Karte, um Datenbankänderungen speichern zu können. + Hängen Sie die SD-Karte ein, um eine Datenbank erstellen oder laden zu können. Version %1$s Geben Sie ein Passwort und/oder eine Schlüsseldatei zum Entsperren Ihrer Datenbank ein. @@ -188,43 +188,43 @@ Groß - Sind Sie sicher, dass Sie ein leeres Passwort verwenden wollen? + Sind Sie sicher, dass das Entsperren ohne Passwort möglich sein soll\? Sind Sie sicher, dass Sie keinen Verschlüsselungsschlüssel verwenden wollen ? Aussehen - Passwortlänge - Standardlänge des generierten Passworts ändern + Generierte Passwortlänge + Legt die Standardlänge des generierten Passworts fest Zwischenablagenbenachrichtigungen Zwischenablagenbenachrichtigungen einschalten um Eingabefelder zu aktivieren Bildschirmsperre Datenbank sperren, wenn der Bildschirm ausgeschaltet wird - KeePass Datei erstellen + Neue Datenbank erstellen Design auswählen Hauptschlüssel zuweisen Pfad Dateiname Dieses Feature konnte nicht gestartet werden. - Datenbanköffnung mit Fingerabdruck einschalten + Ermöglicht die Datenbanköffnung mit Ihrem Fingerabdruck Fingerabdruck Fingerabdruckscanner - Fingerabdruck scannen während das Passwortfeld leer ist, um Datenbank zu öffnen - Fingerabdruck scannen, um Masterpasswort zu speichern - Geben Sie Ihr Passwort in Keepass DX ein + Fingerabdruck scannen, um die Datenbank zu öffnen, wenn die Passworteingabe ausgeschaltet ist. + Fingerabdruck scannen, um Ihr Datenbank-Passwort sicher zu speichern. + Geben Sie Ihr Datenbank-Passwort ein Sperre - Standardzeichen für Passwortgenerator setzen + Erlaubte Zeichen für Passwortgenerator festlegen Passwortzeichen Der Fingerabdruck wird von ihrem Gerät unterstützt, ist jedoch nicht konfiguriert Fingerabdruck wird gescannt Verschlüsseltes Passwort wurde gespeichert - Ungültiger Fingerabdruckschlüssel. Stellen Sie Ihr Passwort wieder her. - Probleme mit dem Fingerabdruck: %1$s + Fingerabdruckschlüssel nicht lesbar. Stellen Sie Ihr Passwort wieder her. + Problem mit dem Fingerabdruck: %1$s Verlauf Wie richte ich den Fingerabdruckscanner für schnelles Entsperren ein? - Speichern Sie Ihren persönlichen Fingerabdruck in + Speichern Sie Ihren für Ihr Gerät eingelesenen Fingerabdruck in \"Einstellungen\" → \"Sicherheit\" → \"Fingerabdruck\" Verwendung Allgemein Fingerabdruck verwenden um dieses Passwort zu speichern - Noch kein Passwort für diese Datenbank gespeichert + Diese Datenbank hat noch kein Passwort. Das App-Design durch Wechsel der Farben ändern Verschlüsselung Schlüsselableitungsfunktion @@ -234,9 +234,9 @@ Autofill-Dienst kann nicht aktiviert werden. Kopie von %1$s Formularausfüllung - Fingerabdruckschlüssel entfernen - Algorithmus um die ganze Datenbank zu verschlüsseln. (Passwörter, Benutzernamen, Notizen und alle Daten in der Datenbank werden mit dem gewählten Algorithmus verschlüsselt) - Um den Schlüssel für den Verschlüsselungsalgorithmus zu generieren, wird der komprimierte Masterschlüssel (SHA-256) mit einer zufällig gesaltenen Schlüsselableitung umgewandelt. + Gespeicherten Fingerabdruck löschen + Verschlüsselungsalgorithmus der Datenbank wird für sämtliche Daten verwendet. + Um den Schlüssel für den Verschlüsselungsalgorithmus zu generieren, wird der Masterschlüssel umgewandelt, wobei ein zufälliger Salt in der Schlüsselberechnung verwendet wird. Speichernutzung Größe des Speichers (in binären Bytes) der für die Schlüsselableitung genutzt wird. Parallelismus @@ -248,54 +248,54 @@ Titel Benutzername Erstellungsdatum - Letztes Änderungsdatum - Letztes Zugriffsdatum + Änderungsdatum + Zugriffsdatum Fingerabdruck nicht erkannt Auto-Einfügen - KeePass DX Autofill-Dienst + KeePass DX autom. Formularausfüllung Mit KeePass DX anmelden Standard Autofill-Dienst auswählen - Dienst aktivieren, um automatisch Eingabefelder anderer Apps auszufüllen + Autofill aktivieren, um automatisch Eingabefelder in anderen Apps auszufüllen Zwischenablage Verschlüsselungskeys löschen Alle Verschlüsselungsschlüssel für Fingerabdruckerkennung löschen - Sind Sie sicher, dass Sie alle Schlüssel für die Fingerabdruckerkennung löschen möchten? + Sind Sie sicher, dass Sie alle mit der Fingerabdruckerkennung verknüpften Schlüssel löschen möchten\? Ihre Android-Version, %1$s, erfüllt nicht die Mindestanforderung, Version %2$s. - Die Hardware wurde nicht erkannt. + Keine entsprechende Hardware. Bytes Dateipfad Vollständigen Dateipfad anzeigen Papierkorb verwenden - Gruppe oder Eintrag in den Papierkorb verschieben bevor er gelöscht wird - KeePass DX benötigt Berechtigungen für Speicherzugriff um Datenbanken zu schreiben - KeePass DX benötigt externe Speicherzugriffsberechtigung um eine URI zu lesen, die nicht von einem Content-Provider zur Verfügung gestellt wird - Speicherzugriff verweigert - Aktion kann ohne Speicherzugriff nicht ausgeführt werden + Verschiebt Gruppen oder Einträge in den Papierkorb, bevor sie gelöscht werden. + KeePass DX benötigt eine Zugriffsberechtigung für den externen Speicher, um in eine Datenbank zu schreiben. + KeePass DX benötigt externe Speicherzugriffsberechtigung, um eine URI zu lesen, die nicht von einem Content-Provider zur Verfügung gestellt wird. + Kein Speicherzugriff möglich. + Aktion kann ohne Speicherzugriff nicht ausgeführt werden. Feldschriftart Schriftart in Feldern ändern, um Lesbarkeit zu verbessern Gewählte Datei automatisch öffnen - Automatisch eine Datei vom Auswahlbildschirm öffnen nach der Auswahl im Dateibrowser - Kopie des Passworts + Dateien nach Auswahl im Dateimanager automatisch öffnen + Zwischenablage vertrauen Kopieren des Passworts und der geschützten Felder in die Zwischenablage erlauben - WARNUNG: Die Deaktivierung dieses Features kann das Öffnen und Speichern von Datenbanken unmöglich machen - Link der zu öffnenden KDBX-Datei + WARNUNG: Die Deaktivierung dieses Features kann das Öffnen und Speichern von Datenbanken unmöglich machen. + Link zu der zu öffnenden Datenbank Datenbankname Datenbankbeschreibung Datenbankversion - Aussehen des Textes - Aussehen der App + Text + App Andere Tastatur Magikeyboard Eine eigene Tastatur zum einfachen Ausfüllen aller Passwort- und Identitätsfelder aktivieren - Hilfe-Anzeige abschalten - Anzeige von Hilfe-Themen abschalten - Hilfe-Anzeige beendet + Hilfe-Anzeige wiederholen + Alle Hilfe-Themen noch einmal anzeigen + Hilfe-Anzeige zurückgesetzt Ihre Datenbankdatei erstellen - Sie kennen KeePass DX noch nicht, erstellen Sie Ihre erste Passwortmanager-Datei. + Erstellen Sie Ihre erste Datei zur Passwortverwaltung. Existierende Datenbank öffnen - Sie haben bereits einen KeePass-Manager verwendet. Einfach die KDBX-Datei über den Dateibrowser öffnen. + Öffnen Sie eine frühere Datenbankdatei über Ihren Dateimanager, um sie weiter zu verwenden. Ein Link zum Speicherort Ihrer Datei ist ausreichend Sie können Ihre Datenbank auch mit einem physischen Link öffnen (z.B. mit file:// und content://). Neue Einträge zu Ihrer Datenbank hinzufügen @@ -350,30 +350,30 @@ Verschieben Einfügen Abbrechen - Sollte Ihr Gerät die Zwischenablage nicht automatisch leeren können, löschen Sie den kopierten Eintrag manuell aus dem Verlauf der Zwischenablage. + Wenn die automatische Löschung der Zwischenablage fehlschlägt, löschen Sie ihren Verlauf manuell. WARNUNG: Alle Apps teilen sich die Zwischenablage. Wenn sensible Daten kopiert werden, kann andere Software darauf zugreifen. Magikeyboard-Einstellungen - Wie funktioniert die Tastaturkonfiguration zum sicheren Ausfüllen von Formularen? - Aktivieren Sie das Magikeyboard in den Geräteeinstellungen. + Tastatur zum sicheren Ausfüllen von Formularen einrichten. + Aktivieren Sie das \"Magikeyboard\" in den Geräteeinstellungen. \"Einstellungen\" → \"Sprache & Eingabe\" → \"Aktuelle Tastatur\" und auswählen. oder (\"Einstellungen\" → \"Sprachen & Eingabe\" → \"Bildschirmtastatur\" und auswählen.) Wählen Sie das Magikeyboard aus, wenn Sie ein Formular ausfüllen müssen. - Sie können leicht von Ihrer Haupttastatur auf Magikeyboard umschalten, entweder mit der Sprachentaste Ihrer Tastatur, durch einen langen Druck auf die Leertaste oder, wenn das nicht zur Verfügung steht, mit: + Wechseln Sie die Tastaturen durch einen langen Druck auf die Leertaste oder, wenn das nicht zur Verfügung steht, mit: Wählen Sie den Eintrag mit dem Schlüssel aus. Füllen Sie die Felder mit den Elementen des Eintrags aus. Sperren Sie die Datenbank. - Kehren Sie zur Haupttastatur zurück. + Kehren Sie zur Standardtastatur zurück. Kein Passwort zulassen - Öffnen-Taste aktivieren, wenn keine Passwort-Identifikation festgelegt ist + \"Öffnen\"-Taste aktivieren, wenn keine Passwort-Identifikation festgelegt ist Hilfe-Anzeige Bedienelemente hervorheben, um die Funktionsweise der App zu lernen - Lesen und Schreiben + veränderbar schreibgeschützt Schreibgeschützt Schreibschutz aktivieren - Datenbanken standardmäßig im schreibgeschützten Modus öffnen + Datenbank standardmäßig schreibgeschützt öffnen Ändern Sie den Modus bei Eröffnung einer Sitzung. \n @@ -381,8 +381,33 @@ \n \nIm Modus ‘Lesen und Schreiben‘ können Sie alle Elemente hinzufügen, löschen oder verändern, wie Sie möchten. Eintrag bearbeiten - Datenbank kann nicht geladen werden - Laden des Schlüssels fehlgeschlagen; versuchen Sie, den Speicher, der von KDF verwendet wird, zu verringen. + Datenbank kann nicht geladen werden. + Laden des Schlüssels fehlgeschlagen; versuchen Sie, die \"Speicherplatznutzung\" von KDF zu verringen. Benutzernamen anzeigen Benutzernamen in Eintragslisten anzeigen + Build %1$s + Magikeyboard + Magikeyboard (KeePass DX) + Magikeyboard-Einstellungen + + Eingabe + + Zeitsperre + Zeit nach der Tastatureingaben gelöscht werden + + Benachrichtigung + Benachrichtigung anzeigen, wenn eine Eingabe abrufbar ist + Eingabe + %1$s über Magikeyboard abrufbar + %1$s + + Beim Schließen löschen + Die Tastatureingabe löschen, wenn die Benachrichtung geschlossen wird + Aussehen + + Tastaturdesign + + Tasten + Vibration auf Tastendruck + Ton auf Tastendruck From 62b077c4b25ecec2a1317b58e3349aa1a3c64586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Fri, 2 Nov 2018 02:28:31 +0000 Subject: [PATCH 005/289] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 99.1% (342 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb/ --- app/src/main/res/values-nb/strings.xml | 27 +++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 2873f1b68..ad3ffb5c9 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -345,4 +345,29 @@ Velg en ikonpakke Endre programmets ikonpakke - \ No newline at end of file +Bygg %1$s + Magikeyboard + Magikeyboard (KeePass DX) + Magikeyboard-innstillinger + + Oppføring + + Tidsavbrudd + Tidsavbrudd for tømming av tastaturoppføring + + Merknadsinfo + Vis en merknad når en oppføring er tilgjengelig + Oppføring + %1$s tilgjengelig på Magikeyboard + %1$s + + Tøm ved lukking + Tøm tastaturoppføringen ved lukking av merknaden + Utseende + + Tastaturdrakt + + Taster + Vibrer ved tastetrykk + Lyd ved tastetrykk + From 5942a33f421d4d3aec267f0cbea949708b51cee1 Mon Sep 17 00:00:00 2001 From: WaldiS Date: Fri, 2 Nov 2018 11:52:16 +0000 Subject: [PATCH 006/289] Translated using Weblate (Polish) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/ --- app/src/main/res/values-pl/strings.xml | 289 ++++++++++++++----------- 1 file changed, 157 insertions(+), 132 deletions(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 5ad65d9df..cd787d383 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -17,33 +17,33 @@ You should have received a copy of the GNU General Public License along with KeePass DX. If not, see . --> - Informacje zwrotne: - Strona domowa: - KeePass DX to Androidowa implementacja menadżera haseł KeePass. + Informacje zwrotne + Strona domowa + Implementacja Android dla menedżera haseł KeePass Akceptuj Dodaj wpis Dodaj grupę - Algorytm + Algorytm szyfrowania Limit czasu aplikacji - Czas do zablokowania bazy danych przy bezczynności aplikacji + Bezczynność przed zablokowaniem aplikacji Aplikacja Ustawienia aplikacji Nawiasy - Przeglądanie plików wymaga programu Open Intents File Manager, kliknij poniżej by go zainstalować. Ze względu na pewne, dziwne zachowania menadżera plików przeglądanie może nie działać poprawnie przy pierwszym użyciu. + Przeglądaj pliki, instalując Menedżera plików OpenIntents Anuluj Schowek został wyczyszczony Czas wygaśnięcia schowka - Czas do wyczyszczenia zawartości schowka po kopiowaniu nazwy użytkownika i hasła + Czas przechowywania w schowku Kopiuj %1$s do schowka Tworzenie klucza bazy danych… Baza danych - Deszyfracja bazy danych… - Używaj tej bazy danych jako domyślnej + Odszyfrowywanie zawartości bazy danych… + Użyj jako domyślnej bazy danych Cyfry - Wprowadź nazwę pliku bazy danych + Otwórz istniejącą bazę danych Dostęp do pliku Anuluj - Komentarz + Notatki Potwierdź hasło Utworzono Wygasa @@ -55,24 +55,24 @@ along with KeePass DX. If not, see . URL Nazwa użytkownika Strumieniowe szyfrowanie ArcFour nie jest wspierane. - KeePass DX nie potrafi obsłużyć tego adresu URL. + Nie można obsłużyć tego identyfikatora URI w KeePass DX. Nie można utworzyć folderu nadrzędnego. Plik o podanej nazwie już istnieje. Nie można otworzyć łącza. - Wymagana nazwa pliku. + Wprowadź nazwę pliku. Nie można utworzyć pliku: - Nieprawidłowa baza danych. - Nieprawidłowa ścieżka dostępu. - Wymagana nazwa. - Wymagane hasło lub plik klucza. - W urządzeniu zabrakło pamięci podczas analizowania bazy danych. - Przynajmniej jeden sposób generowania hasła musi być wybrany + Nie można odczytać bazy danych. + Upewnij się, że ścieżka jest prawidłowa. + Wpisz nazwę. + Wybierz plik klucza. + W urządzeniu zabrakło pamięci do załadowania całej bazy danych. + Należy wybrać co najmniej jeden rodzaj generowania hasła. Hasła nie pasują do siebie. - \"Wartość\" musi być liczbą. - \"Wartość\" za duża. Ustaw 2147483648. - Wymagany tytuł. - Wprowadź dodatnią liczbę całkowitą w polu \"Długość\" - Plik nie istnieje. + \"Rundy szyfrowania\" muszą być liczbą. + \"Rundy szyfrowania\" są zbyt wysokie. Ustaw na 2147483648. + Dodaj tytuł. + Wprowadź dodatnią liczbę całkowitą w polu \"Długość\". + Nie znaleziono pliku. Przeglądarka plików Generuj hasło potwierdź hasło @@ -85,14 +85,14 @@ along with KeePass DX. If not, see . Zainstaluj z Play-Store Zainstaluj z F-Droid Nieprawidłowe hasło lub plik klucza. - Format bazy danych nierozpoznany. + Nie można rozpoznać formatu bazy danych. Długość Wielkość listy grup Wielkość tekstu w liście grup Wczytywanie bazy danych… Małe litery - Maska hasła - Domyślnie ukrywa hasło + Ukryj hasła + Maskuj hasła (***) domyślnie O programie Zmień klucz główny Ustawienia @@ -109,26 +109,26 @@ along with KeePass DX. If not, see . Minus Nigdy Brak wyników wyszukiwania - Brak obsługi dla tego adresu URL. - Otwórz ostatnio używaną bazę danych : + Zainstaluj przeglądarkę internetową, aby otworzyć ten adres URL. + Ostatnio używana baza danych Nie wyszukuj wpisów kopii zapasowej - Pomiń grupę \'Kopia zapasowa\' z wyników wyszukiwania (dotyczy tylko plików .kdb) + Pomiń grupę \"Kopia zapasowa\" z wyników wyszukiwania (dotyczy tylko plików .kdb) Tworzenie nowej bazy danych… Pracuję… Najnowsza historia plików - Zapamiętaj ostatnio używane nazwy plików - Zapamiętaj lokację plików kluczy + Zapamiętaj najnowsze nazwy plików + Zapamiętuje lokalizację plików kluczy baz danych Zapisz plik klucza Usuń Rijndael (AES) Root - Złożoność szyfrowania - Większa złożoność szyfrowania zapewnia dodatkowe zabezpieczenie przed atakiem brute force, ale może spowolnić wczytywanie i zapisywanie bazy danych. - złożoność + Rundy szyfrowania + Dodatkowe rundy szyfrowania zapewniają lepszą ochronę przed atakami typu brute force, ale mogą znacznie spowolnić ładowanie i zapisywanie. + rundy szyfrowania Zapisywanie bazy danych… Spacja Szukaj - Sortuj wg daty + Naturalna baza danych Znaki specjalne Szukaj Dwie ryby @@ -136,13 +136,13 @@ along with KeePass DX. If not, see . Nieobsługiwana wersja bazy danych. Wielkie litery Użyj struktury dostępu do pamięci Android (SAF) do przeglądania plików (KitKat i nowsze) - Dostęp do struktury do pamięci masowej - Karta SD jest obecnie w trybie tylko do odczytu. Możesz nie być w stanie zapisać zmian w bazie danych. - Karta SD nie jest obecnie zamontowana w urządzeniu. Możesz nie być w stanie wczytać lub utworzyć bazy danych. + Dostęp do pamięci masowej + Przydziel dostęp do zapisu na karcie SD, aby zapisać zmiany w bazie danych. + Zamontuj kartę SD, aby utworzyć lub załadować bazę danych. - Wprowadź hasło i/lub plik klucza, aby odblokować bazę danych. -\n -\nPamiętaj o zapisaniu kopii pliku .kdbx w bezpiecznym miejscu po każdej modyfikacji. + prowadź hasło i/lub plik klucza, aby odblokować bazę danych. +\n +\nUtwórz kopię zapasową pliku bazy danych w bezpiecznym miejscu po każdej zmianie. 5 sekund @@ -163,44 +163,44 @@ along with KeePass DX. If not, see . Edytuj wpis Dodaj ciąg Szyfrowanie - Funkcja Generująca Klucze + Funkcja generująca klucz Nie pokazuj więcej Rozszerzone ASCII Zezwalaj Błąd schowka - Niektóre telefony Samsung z Androidem mają błąd w implementacji schowka, co powoduje, że kopiowanie z aplikacji kończy się niepowodzeniem. Więcej szczegółów: - Czyszczenie schowka nie powiodło się + Niektóre telefony Samsung z Androidem nie pozwalają aplikacjom korzystać ze schowka. + Nie można wyczyścić schowka Przesuń, by wyczyścić schowek - KeePass DX \u00A9 %1$d Kunzisoft korzystasz ABSOLUTNIE BEZ GWARANCJI; To jest bezpłatne oprogramowanie i możesz go redystrybuować na warunkach GPL w wersji 3 lub późniejszej. + Z aplikacji KeePass DX © %1$d Kunzisoft korzystasz ABSOLUTNIE BEZ GWARANCJI; To jest wolne oprogramowanie i możesz go redystrybuować na warunkach GPL w wersji 3 lub późniejszej. Nie znaleziono danych wejściowych. - Baza danych nie może być załadowana - Nie można załadować klucza, spróbuj zmniejszyć pamięć używaną przez KDF. + Nie można załadować bazy danych. + Nie można załadować klucza. Spróbuj zmniejszyć użycie pamięć KDF. Każdy ciąg musi mieć nazwę pola. - Usługa autofillowania nie może być włączona. + Nie można włączyć usługi autouzupełniania. Nie można przenieść grupy do samej siebie. Nazwa pola Wartość pola - Nie znaleziono pliku. Spróbuj ponownie otworzyć zawartość od dostawcy treści. - Nieprawidłowy algorytm. + Nie znaleziono pliku. Spróbuj ponownie otworzyć go w przeglądarce plików. + Błędny algorytm. Brak pliku klucza. Plik klucza jest pusty. - Wyświetlaj nazwy użytkownika - Wyświetlaj nazwy użytkowników na listach wpisów + Pokaż nazwy użytkowników + Pokaż nazwy użytkowników na listach wpisów Kopiuj z %1$s Wypełnianie formularzy Kopiuj Przenieś Wklej Anuluj - Usuń klucz linii papilarnych - Tylko do odczytu - Odczyt i zapis + Usuń zapisany klucz linii papilarnych + Chroniony przed zapisem + Modyfikowalne Ochrona - Tylko do odczytu - KeePass DX nie ma uprawnień do zapisu w Twojej lokalizacji bazy danych, więc zostanie otwarty tylko do odczytu. + Chroniony przed zapisem + KeePass DX potrzebuje uprawnień do zapisu, aby mógł modyfikować bazę danych. Począwszy od Androida KitKat niektóre urządzenia nie zezwalają już aplikacjom na zapisywanie na kartę SD. - Algorytm do szyfrowania całej bazy danych. (Hasła, nazwy użytkowników, notatki i wszystkie dane w bazie danych są szyfrowane za pomocą wybranego algorytmu) - Aby wygenerować klucz do algorytmu szyfrowania, skompresowany klucz główny (SHA-256) jest transformowany przy użyciu losowo solonej funkcji wyprowadzania klucza. + Algorytm szyfrowania bazy danych używany dla wszystkich danych. + Aby wygenerować klucz dla algorytmu szyfrowania, klucz główny jest transformowany przy użyciu losowo solonej funkcji wyprowadzania klucza. Użycie pamięci Ilość pamięci (w bajtach binarnych) do użycia przez funkcję wyprowadzania klucza. Równoległy @@ -208,137 +208,137 @@ along with KeePass DX. If not, see . Sortuj Od najniższej ↓ Nazwa Użytkownika - Czas utworzenia - Ostatni czas modyfikacji - Ostatni czas dostępu + Utwórz + Modyfikacja + Dostęp Wyniki wyszukiwania Ostrzeżenie - Twoje hasło może zawierać litery nieobsługiwane przez zestaw znaków Latin-1 używany przez pliki .kdb. Ponieważ wszystkie są konwertowane na tę samą literę, zaleca się zmianę hasła, aby było bezpieczniejsze. + Unikaj znaków w hasłach spoza Latin-1 w plikach używanych przez pliki .kdb, ponieważ wszystkie są konwertowane na tę samą literę. Kosz na dole Tytuł - Czy na pewno chcesz użyć pustego ciągu jako hasła? + Czy naprawdę nie chcesz ochrony przed odblokowaniem hasła\? Czy na pewno nie chcesz używać żadnego klucza szyfrowania? Wersja %1$s - Odcisk palca jest obsługiwany, ale nie skonfigurowany dla tego urządzenia + Skanowanie odcisków palców jest obsługiwane, ale nie skonfigurowane. "Przechowywane hasło zaszyfrowane " Grupy poprzednie - Odczytywanie odcisków palców - Problem z kluczem linii papilarnych. Przywróć swoje hasło. - Odcisk palca nie został rozpoznany - Problem z odciskami palców: %1$s + Skanowanie odcisków palców + Nie można odczytać klucza linii papilarnych. Przywróć swoje hasło. + Nie można rozpoznać odcisku palca + Problem z odciskiem palca: %1$s Użyj odcisku palca, aby zapisać to hasło - Nie ma jeszcze zapisanego hasła dla tej bazy danych + Baza danych nie ma jeszcze hasła. Historia Wygląd Ogólne Wypełnij automatycznie - Usługa autouzupełniania KeePass DX + Autouzupełnianie formularzy KeePass DX Zaloguj się za pomocą KeePass DX Ustaw domyślną usługę autouzupełniania - Włącz usługę, aby łatwo wypełniać formularze z innych aplikacji - Rozmiar hasła - Ustaw domyślny rozmiar generowanego hasła + Włącz autouzupełnianie, aby móc szybko wypełniać formularze w innych aplikacjach + Wygenerowany rozmiar hasła + Ustawia domyślny rozmiar wygenerowanych haseł Znaki hasła - Ustaw domyślne znaki generatora haseł + Ustaw dozwolone znaki generatora haseł Schowek - Powiadomienia w schowku + Powiadomienia ze schowka Włącz powiadomienia ze schowka, aby skopiować pola wprowadzania - Jeśli urządzenie nie może automatycznie usunąć klipów ze schowka, ręcznie usuń skopiowany element z historii schowka. + Jeśli automatyczne usuwanie schowka nie powiedzie się, ręcznie usuń jego historię. Blokada Blokada ekranu Zablokuj bazę danych, gdy ekran jest wyłączony - Jak skonfigurować odcisk palca do szybkiego odblokowania? - Ustaw swój odcisk palca dla swojego urządzenia + Jak skonfigurować skanowanie linii papilarnych do szybkiego odblokowania\? + Zapisz zeskanowany odcisk palca w urządzeniu \"Ustawienia\" → \"Bezpieczeństwo\" → \"Odcisk palca\" - Wpisz swoje hasło w Keepass DX - Zeskanuj swój odcisk palca, aby bezpiecznie przechowywać hasło główne - Zeskanuj swój odcisk palca, gdy pole wyboru hasła nie jest zaznaczone, aby otworzyć bazę danych + Wprowadź hasło blokady bazy danych + Zeskanuj swój odcisk palca, aby bezpiecznie zapisać hasło blokady bazy danych. + Zeskanuj swój odcisk palca, aby otworzyć bazę danych po wyłączeniu hasła. Użycie Odcisk palca - Skanowanie w poszukiwaniu odcisków palców - Włącz tworzenie bazy danych przez odcisk palca + Skanowanie odcisków palców + Umożliwia skanowanie odcisku palca w celu otwarcia bazy danych Usuń klucze szyfrowania Usuń wszystkie klucze szyfrowania związane z rozpoznawaniem linii papilarnych - Czy na pewno chcesz usunąć wszystkie klucze związane z odciskami palców? + Czy na pewno chcesz usunąć wszystkie klucze związane z rozpoznawaniem linii papilarnych\? Nie można uruchomić tej funkcji. Twoja wersja Androida %1$s nie spełnia wymaganej minimalnej wersji %2$s. - Sprzęt nie został wykryty. + Nie można znaleźć odpowiedniego sprzętu. Nazwa pliku Ścieżka Przypisz klucz główny - Utwórz plik KeePass + Utwórz nową bazę danych Bajtów Ścieżka pliku Wyświetl pełną ścieżkę do pliku Użyj kosza - Przenieś grupę lub wpis do \"Kosza\" przed usunięciem - KeePass DX potrzebuje zewnętrznego pozwolenia na przechowywanie, aby zapisać bazę danych - KeePass DX potrzebuje zewnętrznego pozwolenia na przechowywanie, aby odczytać URI, który nie został dostarczony przez dostawcę treści - Odmowa dostępu do zewnętrznej pamięci masowej - Nie można wykonać akcji bez uprawnień do zewnętrznego magazynu + Przenosi grupy i wpisy do \"Kosza\" przed usunięciem + KeePass DX potrzebuje zewnętrznego pozwolenia na przechowywanie, aby zapisać bazę danych. + KeePass DX potrzebuje zewnętrznego pozwolenia na przechowywanie, aby odczytać URI, który nie został dostarczony przez dostawcę treści. + Nie można uzyskać uprawnień do zewnętrznej pamięci masowej. + Nie można wykonać akcji bez uprawnień do zewnętrznej karty pamięci. Pole czcionka Zmień czcionkę użytą w polach, aby poprawić widoczność postaci - Automatycznie otwórz wybrany plik - Automatycznie otwórz plik z ekranu wyboru, bezpośrednio po dokonaniu wyboru w eksploratorze plików - Kopia hasła - Zezwalaj na kopiowanie hasła i chronionych pól do schowka + Otwórz pliki, wybierając + Automatyczne otwieranie plików po wybraniu w przeglądarce plików + Zaufanie do schowka + Zezwalaj na zapisywanie hasła dostepu i chronionych pól do schowka OSTRZEŻENIE: schowek jest udostępniany przez wszystkie aplikacje. Jeśli dane wrażliwe zostaną skopiowane, inne oprogramowanie może je odzyskać. - OSTRZEŻENIE: Wyłączenie tej funkcji może spowodować niemożność otwarcia lub zapisania baz danych - Link do otwarcia pliku KDBX + OSTRZEŻENIE: Wyłączenie tej funkcji może uniemożliwić otwieranie lub zapisywanie baz danych. + Link do otwarcia pliku bazy danych Nazwa bazy danych Opis bazy danych Wersja bazy danych - Wygląd tekstu - Wygląd aplikacji + Tekst + Aplikacja Inne Klawiatura Magikeyboard - Aktywuj niestandardową klawiaturę, która łatwo wypełnia hasła i wszystkie pola tożsamości + Aktywuj niestandardową klawiaturę wypełniającą hasła i wszystkie pola tożsamości Ustawienia Magikeyboard - Jak skonfigurować klawiaturę do bezpiecznego wypełniania formularzy? - Aktywuj Magikeyboard w ustawieniach urządzenia. + Skonfiguruj klawiaturę, aby bezpiecznie wypełniać formularze. + Aktywuj \"Magikeyboard\" w ustawieniach urządzenia. \"Ustawienia\" → \"Język i wprowadzanie\" → \"Aktualna klawiatura\" i wybierz jedną. lub (\"Ustawienia\" → \"Język i wprowadzanie danych\" → \"Klawiatura wirtualna\" i wybierz jedną.) Wybierz Magikeyboard, gdy potrzebujesz wypełnić formularz. - Możesz łatwo przełączać się z głównej klawiatury na Magikeyboard za pomocą przycisku języka na klawiaturze, długie naciśnięcie klawisza spacji na klawiaturze lub, jeśli nie jest dostępne, z: + Przełącz klawiaturę naciskając klawisz spacji na klawiaturze lub, jeśli nie jest dostępny, z: Wybierz wpis za pomocą klawisza. - Wypełnij swoje pola, używając elementów wpisu. + Wypełnij swoje pola, używając elementów pozycji. Zablokuj bazę danych. - Wróć do głównej klawiatury. + Użyj ponownie domyślnej klawiatury. Nie zezwalaj na żadne hasło - Włącz przycisk Otwórz, jeśli nie wybrano identyfikacji hasła - Tylko do odczytu + Włącz przycisk \"Otwórz\", jeśli nie wybrano identyfikacji hasła + Ochrona przed zapisem Domyślnie otwarte bazy danych są tylko do odczytu Ekrany edukacyjne Zaznacz elementy, aby dowiedzieć się, jak działa aplikacja Zresetuj ekrany edukacyjne - Zresetuj wyświetlanie elementów edukacyjnych + Pokaż ponownie wszystkie elementy edukacyjne Ekrany edukacyjne zostały zresetowane Utwórz plik bazy danych - Nie znasz jeszcze KeePass DX, utwórz swój pierwszy plik zarządzania hasłami. + Utwórz swój pierwszy plik zarządzania hasłami. Otwórz istniejącą bazę danych - Użyłeś już menedżera KeePass. Wystarczy otworzyć plik KDBX z przeglądarki plików. + Otwórz starszy plik bazy danych w przeglądarce plików, aby nadal z niego korzystać. Link do lokalizacji pliku jest wystarczający Możesz także otworzyć swoją bazę danych za pomocą fizycznego linku (with file:// and content:// for example). - Dodaj nowe przedmioty do swojej bazy + Dodaj elementy do swojej bazy danych "Dodaj wpisy, aby zarządzać swoimi cyfrowymi tożsamościami. \n \nDodaj grupy (odpowiednik folderów), aby uporządkować swoje wpisy i bazę danych." - Łatwo przeszukuj swoje wpisy - Wyszukaj wpisy według tytułu, nazwy użytkownika lub innych pól, aby łatwo odzyskać swoje hasła. + Przeszukuj wpisy + Wprowadź tytuł, nazwę użytkownika lub zawartość innych pól, aby odzyskać swoje hasła. Odblokuj bazę danych za pomocą odcisku palca - Utwórz łącze między hasłem a odciskiem palca, aby łatwo odblokować bazę danych. + Połącz swoje hasło z zeskanowanym odciskiem palca, aby szybko odblokować bazę danych. Edytuj wpis - Edytuj swój wpis własnymi polami, odniesienia do danych puli mogą być dodawane pomiędzy polami różnych wpisów. - Utwórz silne hasło - Wygeneruj silne hasło do powiązania z wpisem, określ je zgodnie z kryteriami formularza i nie zapominaj o bezpiecznym haśle, które możesz zapamiętać. + Edytuj swój wpis za pomocą pól niestandardowych. Dane puli mogą być przywoływane między różnymi polami wprowadzania. + Utwórz silne hasło do swojego wpisu. + Wygeneruj silne hasło do powiązania z wpisem, z łatwością określ je zgodnie z kryteriami formularza i nie zapomnij o bezpiecznym haśle. Dodaj niestandardowe pola - Chcesz zarejestrować podstawowe pole nie dostarczane, po prostu wypełnij nowe, które możesz również chronić wizualnie. + Zarejestruj podstawowe pole niedostarczone, wypełniając nowe pole, które możesz również chronić. Odblokuj swoją bazę danych - Włącz tylko do odczytu + Zapisz ochronę swojej bazy danych "Zmień tryb otwierania sesji. \n \nW trybie tylko do odczytu zapobiegasz niezamierzonym zmianom w bazie danych. @@ -349,13 +349,13 @@ along with KeePass DX. If not, see . \n \nMożesz użyć kilku metod wypełniania formularzy. Użyj tego, który wolisz." Zablokuj bazę danych - Zablokuj swoją bazę danych szybko, możesz skonfigurować aplikację, by po chwili zablokowała i po wyłączeniu ekranu. - Sortuj elementy - Sortuj wpisy i grupy według określonych parametrów. + Szybko zablokuj bazę danych, możesz skonfigurować aplikację, by po chwili ją zablokować, i gdy ekran się wyłączy. + Sortowanie elementów + Wybierz sposób sortowania wpisów i grup. Weź udział - Weź udział, aby zwiększyć stabilność, bezpieczeństwo i dodać więcej funkcji. + Pomóż zwiększyć stabilność, bezpieczeństwo i dodawanie kolejnych funkcji. - W przeciwieństwie do wielu aplikacji zarządzania hasłami, ten jeden jest bez reklam, darmowy program typu copyleft i nie odzyskać danych osobowych na swoich serwerach, nawet w jego wersji darmowej. + W przeciwieństwie do wielu aplikacji do zarządzania hasłami, ta jest wolna od reklam, jest oprogramowaniem darmowym typu copylefted libre i nie zbiera danych osobowych na swoich serwerach, bez względu na to, jakiej wersji używasz. Kupując wersję pro, będziesz mieć dostęp do tej funkcja wizualna a szczególnie pomożesz zrealizować projekty społecznościowe. Ta funkcja wizualna jest dostępna dzięki Twojej hojności. Aby zachować naszą wolność i być zawsze aktywnym, liczymy na Twój wkład. @@ -366,7 +366,7 @@ along with KeePass DX. If not, see . zachęcasz programistów do tworzenia nowych funkcji i naprawić błądy zgodnie z Twoimi uwagami. Wielkie dzięki za twój wkład. Ciężko pracujemy, aby szybko udostępnić tę funkcję. - Nie zapomnij o aktualizowaniu aplikacji. + Nie zapomnij o aktualizacji aplikacji, instalując nowe wersje. Pobieranie Przyczyń się @@ -376,9 +376,34 @@ along with KeePass DX. If not, see . AES KDF Argon2 - Wybierz motyw - Zmień motyw aplikacji, zmieniając kolory - Wybierz pakiet ikon - Zmień pakiet ikon aplikacji + Motyw aplikacji + Motyw używany w aplikacji + Pakiet ikon + Pakiet ikon używany w aplikacji - +Kompilacja %1$s + Magikeyboard + Magikeyboard (KeePass DX) + Ustawienia Magikeyboard + + Wpis + + Limit czasu + Limit czasu, aby wyczyścić wpis klawiatury + + Informacje dotyczące powiadomień + Pokaż powiadomienie, gdy wpis jest dostępny + Wpis + %1$s dostępne na Magikeyboard + %1$s + + Wyczyść przy zamykaniu + Wyczyść wpis klawiatury podczas zamykania powiadomienia + Wygląd + + Motyw klawiatury + + Klawiatura + Wibracja po naciśnięciu klawisza + Dźwięk przy naciśnięciu + From 526111af9821dc1261f5609572756d6aa46021ca Mon Sep 17 00:00:00 2001 From: jan madsen Date: Tue, 6 Nov 2018 09:36:44 +0000 Subject: [PATCH 007/289] Translated using Weblate (Danish) Currently translated at 98.6% (340 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/ --- app/src/main/res/values-da/strings.xml | 111 +++++++++++++------------ 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index d805083b9..7788ebe28 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -40,7 +40,7 @@ Nogle Samsung Android-telefoner, vil ikke lade programmer bruge udklipsholderen. Kunne ikke rydde udklipsholderen Udklipsholder timeout - Varighed af opbevaring i udklipsholderen + Varighed af opbevaring i udklipsholder Vælg for at kopiere %1$s til udklipsholder Opretter databasenøgle… Database @@ -141,12 +141,12 @@ Rijndael (AES) Rod Transformationsrunder - Højere antal krypteringsrunder giver øget beskyttelse imod brute-force angreb, men kan påvirke læsnings- og skrivehastigheden betydentligt. + "Yderligere krypteringsrunder giver højere beskyttelse mod brute-force angreb, men kan virkelig forsinke læsnings- og skrivehastigheden." Transformationsrunder Gemmer database… Mellemrum Søg - DB sorteringsrækkefølge + Naturlig database Speciel Søg Søgeresultater @@ -154,8 +154,8 @@ Understregning Database-versionen er ikke understøttet. Store bogstaver - SD-kortet er i øjeblikket skrivebeskyttet, kan muligvis ikke gemme ændringer i databasen. - SD-kortet er ikke monteret i enheden, kan ikke indlæse eller oprette en database. + Giv SD-kort skrive adgang for at gemme databasen ændringer. + Monter SD-kortet for at oprette eller indlæse en database. Version %1$s Angiv en adgangskode og/eller en nøglefil til at låse databasen op. \n @@ -201,7 +201,7 @@ Skrivebeskyttet Modificerbar Skrivebeskyttet - Algoritme til kryptering af hele databasen. (adgangskoder, brugernavne, noter og alle data i databasen er krypteret med den valgte algoritme) + Databasekrypteringsalgoritme anvendt for alle data. For at generere nøglen til krypteringsalgoritmen, omdannes hovednøglen ved hjælp af en tilfældigt saltet nøgleafledningsfunktion. Hukommelsesforbrug Hukommelse (i binær byte), som anvendes af nøgleafledningsfunktion. @@ -219,52 +219,52 @@ Brug Android Storage Access (SAF) som filhåndtering (KitKat og senere) Storage Access Framework Advarsel - Adgangskoden kan indeholde bogstaver der ikke understøttes af det Latin-1 tegnsæt, der bruges af .kdb filer. Da alle disse konverteres til samme bogstav, anbefales det at ændre adgangskoden for at gøre det sikrere. - Brug en tom streng som adgangskode? + Undgå adgangskodetegn undenfor Latin-1 tegnsæt i .kdb filer, da de alle konverteres til det samme bogstav. + Bekræft brug af ingen adgangskode til beskyttelse mod oplåsning\? Bekræft ingen brug af en krypteringsnøgle? - Fingeraftryk er understøttet, men ikke konfigureret for denne enhed + Fingeraftryksscanning understøttes, men er ikke sat op. Fingeraftryks-scanning Krypteret adgangskode er gemt - Ugyldig fingeraftryksnøgle. Gendan adgangskode. + Kunne ikke læse fingeraftryksnøgle. Gendan adgangskode. Kunne ikke genkende fingeraftryk Problem med fingeraftryk: %1$s Brug fingeraftryk til at gemme adgangskoden - Ingen adgangskode er endnu gemt i databasen + Databasen har endnu ikke en adgangskode. Historik Udseende Generelt Autoudfyld - KeePass DX Autoudfyld tjeneste + KeePass DX formularudfyldning Log ind med KeePass DX - Indstil standard Autoudfyld - Aktiver tjenesten for nemt at udfylde formularer fra andre apps - Adgangskode størrelse - Angiv standardlængden for den genererede adgangskode + Indstil standard autoudfyldservice + Aktiver autofyldning for hurtigt at udfylde formularer i andre programmer + Genereret kodeordslængde + Angiver standardlængden for genererede adgangskoder Adgangskodetegn - Angiv standardtegn for adgangskodegenerator + Angiv tilladte tegn for adgangskodegenerator Udklipsholder Udklipsholdermeddelelser Aktivere meddelelser fra udklipsholder for at kopiere indtastningsfelter - Hvis enheden ikke er i stand til at slette udklip fra udklipsholder automatisk, så slet det kopierede element manuelt fra udklipsholder historikken. + Hvis automatisk sletning af udklipsholder mislykkes, slet historikken manuelt. Lås Skærmlås Lås databasen, når skærmen er slukket - Hvordan konfigureres fingeraftryk til hurtig oplåsning? - Indstil personligt fingeraftryk for enhed + Hvordan konfigureres fingeraftryksscanning til hurtig oplåsning\? + Gem scannede fingeraftryk for enhed i \"Indstillinger\" → \"Sikkerhed\" → \"Fingeraftryk\" - Indtast adgangskoden til Keepass DX - Scan fingeraftryk for at gemme hovedadgangskoden sikkert - Scan fingeraftryk, når afkrydsningsfeltet for adgangskoden ikke er markeret, for at åbne databasen + Indtast adgangskoden til at låse databasen + Scan fingeraftryk for at gemme dataseadgangskode sikkert. + Scan fingeraftryk til at åbne databasen, når adgangskoden er slået fra. Brug Fingeraftryk - Fingeraftryks-scanning - Aktiver åbning af databasen med fingeraftryk + Fingeraftryksscanning + Scan fingeraftryk for at åbne databasen Slet krypteringsnøgler Slet alle krypteringsnøgler, der er relateret til fingeraftryk - Bekræft sletning af alle nøgler, der er relateret til fingeraftryk? + Bekræft sletning af alle nøgler, der er relateret til fingeraftryksgenkendelse\? Funktionen kunne ikke startes. Android-version %1$s opfylder ikke minimum versionskrav %2$s. - Hardwaren ikke fundet. + Kunne ikke finde den tilsvarende hardware. Filnavn Sti Tildel en hovednøgle @@ -276,16 +276,16 @@ Flyt grupper og poster til \"Papirkurven\" før den slettes KeePass DX skal have tilladelse til ekstern lagring for at skrive til en database. KeePass DX skal have tilladelse til ekstern lagring for at læse en URI, der ikke leveres af en indholdsleverandør. - Tilladelse til ekstern lager nægtet + Tilladelse til ekstern lager nægtet. Kan ikke udføre handlingen uden ekstern lager tilladelse. Feltskrifttype Skift skrifttypen, der anvendes i felter, for at forbedre tegnsynlighed Åbn filer ved at vælge Åbn filer automatisk efter valg i filhåndtering - Kopi af adgangskode - Tillader kopiering af adgangskoden og beskyttede felter til udklipsholder + Udklipsholder tillid + Tillad at adgangskoden og beskyttede felter kopieres til udklipsholderen ADVARSEL: Udklipsholder deles af alle apps. Hvis følsomme data er kopieret, kan andet software gendanne den. - ADVARSEL: Deaktivering af funktionen kan resultere i en manglende evne til at åbne eller gemme databaser + ADVARSEL: Deaktivering af funktionen kan forhindre åbne eller gemme databaser. Link til database-filen der skal åbnes Databasenavn Database beskrivelse @@ -296,33 +296,35 @@ Tastatur Magikeyboard - Aktiver et brugerdefineret tastatur, der nemt udfylder adgangskoder og alle identitetsfelter + Aktiver et brugerdefineret tastatur, der udfylder adgangskoder og alle identitetsfelter Magikeyboard indstillinger - Hvordan konfigureres tastaturet til sikker formularudfyldning? + Hvordan konfigureres tastaturet til sikker formularudfyldning\? +\n +\nOpsæt tastaturet til autofyld af formularerne sikkert. Aktiver Magikeyboard\" i enhedens indstillinger. \"Indstillinger\" → \"Sprog & input\" → \"Aktuelt tastatur\" og vælg et. eller (\"Indstillinger\" → \"Sprog & input\" → \"Virtuelt tastatur\" og vælg et.) Vælg Magikeyboard, når der er brug for at udfylde en formular. - Skift nemt fra det primære tastatur til Magikeyboard med sprog-knappen på tastaturet, et langt tryk på mellemrumstasten på tastaturet eller - hvis det ikke er tilgængelig - med: + Skift tastatur med et langt tryk på mellemrumstasten på tastaturet eller -hvis det ikke er tilgængelig - med: Vælg post med nøglen. Udfyld felterne ved hjælp af elementerne i posten. Lås databasen. - Gå tilbage til det primære tastatur. + Brug standard tastaturet igen. Tillad ingen adgangskode Aktiver knappen \"Åbn\", hvis der ikke er valgt en adgangskodeidentifikation Skrivebeskyttet - Som standard åbne databaser skrivebeskyttet + Åbn database skrivebeskyttet som standard Hjælpeskærme Fremhæver elementer for at lære, hvordan programmet fungerer Nulstil hjælpeskærme - Nulstil visning af hjælpeelementer + "Vis alle hjælpeelementer igen" Hjælpeskærme nulstillet Opret databasefilen - Kender endnu ikke KeePass DX, opret den første adgangskodeadministrationsfil. + Opret den første adgangskodeadministrationsfil. Åbn en eksisterende database - Har allerede brugt en KeePass manager. Bare åbn KDBX filen fra filhåndtering. + Åbn den tidligere database fil fra filhåndtering for at fortsætte med at bruge den. Et link til filen er tilstrækkelig Databasen kan også åbnes med en fysisk forbindelse (med file:// og content:// for eksempel). Tilføj elementer til databasen @@ -330,17 +332,17 @@ \n \nTilføje grupper (svarende til mapper) for at organisere indtastninger og database. Søg i poster - Søg efter poster efter titel, brugernavn, eller på andre felter for nemt at hente adgangskoder. - Lås databasen op med fingeraftryk - Lav en forbindelsen mellem kodeord og fingeraftryk for nemt at låse databasen op. + Indtast titel, brugernavn eller indhold af andre felter for at hente adgangskoder. + Database oplåsning med fingeraftryk + Link adgangskoden til det scannede fingeraftryk for hurtigt at låse databasen op. Rediger posten - Rediger post med brugerdefinerede felter, henvisninger til pooldata kan tilføjes mellem felter i forskellige poster. - Opret en stærk adgangskode + Rediger post med brugerdefinerede felter. Pool data kan refereres mellem forskellige indtastningsfelter. + Opret en stærk adgangskode til posten. Generer en stærk kodeord til at forbinde elementet, definer det i henhold til kriteriet for formularen og glem ikke et sikkert kodeord. Tilføj brugerdefinerede felter - Registrer et felt, der ikke allerede eksisterer, indtast blot i det den nye, som også kan beskyttes visuelt. + Registrer et grundlæggende felt, der ikke er oprettet, ved at udfylde et ny, som også kan beskyttes. Lås databasen op - Aktiver skrivebeskyttelse + Skrivebeskyt databasen "Skift åbningstilstanden for sessionen. \n  \nI skrivebeskyttet tilstand forhindres utilsigtede ændringer i databasen. @@ -353,11 +355,11 @@ Lås databasen Lås hurtigt databasen, indstil til at låse efter et stykke tid, og når skærmen slukkes. Sorter elementer - Sorter poster og grupper i henhold til specifikke parametre. + Vælg hvordan poster og grupper er sorteret. Deltag Bidrag til at øge stabiliteten, sikkerheden og med at tilføje flere funktioner. - I modsætning til andre programmer til adgangskodeadministration er denne annoncefri , copyleft fri software, og indsamler ikke personlige data på servere, selv i den gratis version. + I modsætning til andre programmer til adgangskodeadministration er denne annoncefri , copyleft fri software", og indsamler ikke personlige data, uanset hvilken version der bruges." Ved at købe pro-versionen, er der adgang til visuel funktionen, og det vil især hjælpe gennemførelsen af lokale projekter. Denne visuelle funktion er tilgængelige takket være bidrag. For at bevare uafhængighed og altid at være aktiv, regner vi medbidrag. @@ -368,7 +370,7 @@ tilskyndes udviklerne til at lave nye funktioner og rette fejl i henhold bemærkninger. Tak for bidrag. Vi arbejder hårdt på hurtigt at frigive denne funktion. - Glem ikke at holde appen opdateret. + Glem ikke at holde appen opdateret ved at installere nye versioner. Hent Bidrag @@ -378,10 +380,10 @@ AES KDF Argon2 - Vælg et tema - Ændre tema ved at ændre farver + Tema + Tema, der bruges i programmet Ikonpakke - Skift ikonpakke + "Ikonpakke, der anvendes " Magikeyboard Magikeyboard (KeePass DX) @@ -397,7 +399,7 @@ %1$s Ryd ved lukning - Ryd tastatur indlæg, når meddelse lukkes + Ryd tastatur post, når meddelse lukkes Udseende Tastaturtema @@ -405,4 +407,7 @@ Taster Vibrer ved tastetryk Lyd ved tastetryk + Build %1$s + Timeout for at rydde indtastning + From b09bf68b7c21407f69e81dfb688c5b5b101c25a2 Mon Sep 17 00:00:00 2001 From: Kunzisoft Date: Wed, 7 Nov 2018 18:11:56 +0000 Subject: [PATCH 008/289] Translated using Weblate (French) Currently translated at 50.4% (174 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 72c84f25c..70273b683 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -29,7 +29,7 @@ Algorithme de chiffrement Fonction de dérivation de clé Délai d\'expiration de l\'application - Temps avant le verrouillage de la base de données lorsque l\'application est inactive + Inactivité avant le verrouillage de l\'application Application Préférences de l\'application Remplissage de formulaire From 2253d3c2cc5fcba81f5557b1ec83d47af9991df2 Mon Sep 17 00:00:00 2001 From: ssantos Date: Wed, 7 Nov 2018 18:18:43 +0000 Subject: [PATCH 009/289] Translated using Weblate (German) Currently translated at 97.4% (336 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 17ce29cc6..63c5fce2e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -50,7 +50,7 @@ Entschlüsselung der Datenbankinhalte … Als Standard-Datenbank benutzen Zahlen - KeePass DX \u00A9 %1$d Kunzisoft. Alle Rechte vorbehalten. Die Nutzung der Software erfolgt auf eigene Verantwortung und ohne jegliche Garantie. Die Applikation ist kostenlos und wird unter den Bedingungen der GNU GPL Version 3 (oder später) verbreitet und lizenziert. + KeePass DX © %1$d Kunzisoft. Alle Rechte vorbehalten. Die Nutzung der Software erfolgt auf eigene Verantwortung und ohne jegliche Garantie. Die Applikation ist frei und wird unter den Bedingungen der GNU GPL Version 3 (oder später) verbreitet und lizenziert. Vorhandene Datenbank öffnen Letzter Zugriff Abbrechen @@ -80,8 +80,8 @@ Zu wenig Speicherplatz, um die ganze Datenbank zu laden. Mindestens eine Art der Passwortgenerierung muss ausgewählt werden. Die Passwörter stimmen nicht überein. - Die Anzahl der Wiederholungen muss eine Zahl sein. - Die maximale Wiederholungsanzahl ist 2147483648. + Mache \"Transformationsrunden\" zu einer Zahl. + \"Transformationsrunden\" zu hoch. Stelle ein auf 214748364848. Für jede Zeichenfolge ist ein Feldname notwendig. Titel hinzufügen. Geben Sie die erforderliche Länge als positive, ganze Zahl in das Feld ein. @@ -140,7 +140,7 @@ Ab Android KitKat haben auf einigen Geräten Apps keine Schreibrechte mehr für die SD-Karte. Zuletzt verwendete Datenbanken Zuletzt verwendete Datenbanken merken - Den Pfad der Schlüsseldatei merken + Erinnert sich an den Speicherort der Schlüsseldateien der Datenbanken Schlüsselquelle merken Löschen Rijndael (AES) @@ -151,7 +151,7 @@ Speichere Datenbank\u2026 Leerzeichen Suchen - Sortieren nach Erstellung + Natürliche Datenbank Spezialsymbole Titel/Beschreibung des Eintrags Suchergebnisse @@ -247,7 +247,7 @@ Papierkorb unten Titel Benutzername - Erstellungsdatum + Erzeugt am Änderungsdatum Zugriffsdatum Fingerabdruck nicht erkannt From 316369f1174f7b67facb32230fec32a4ff311de8 Mon Sep 17 00:00:00 2001 From: Krombel Date: Thu, 8 Nov 2018 13:00:34 +0000 Subject: [PATCH 010/289] Translated using Weblate (German) Currently translated at 97.4% (336 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 45 +++++++++++++------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 63c5fce2e..1ebc62cc7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -198,7 +198,7 @@ Bildschirmsperre Datenbank sperren, wenn der Bildschirm ausgeschaltet wird Neue Datenbank erstellen - Design auswählen + App-Design Hauptschlüssel zuweisen Pfad Dateiname @@ -212,7 +212,7 @@ Sperre Erlaubte Zeichen für Passwortgenerator festlegen Passwortzeichen - Der Fingerabdruck wird von ihrem Gerät unterstützt, ist jedoch nicht konfiguriert + Fingerabdruck-Scannen wird unterstützt, ist jedoch nicht konfiguriert Fingerabdruck wird gescannt Verschlüsseltes Passwort wurde gespeichert Fingerabdruckschlüssel nicht lesbar. Stellen Sie Ihr Passwort wieder her. @@ -225,7 +225,7 @@ Allgemein Fingerabdruck verwenden um dieses Passwort zu speichern Diese Datenbank hat noch kein Passwort. - Das App-Design durch Wechsel der Farben ändern + App-Design, welches in der App genutzt wird Verschlüsselung Schlüsselableitungsfunktion Erweitertes ASCII @@ -298,18 +298,20 @@ Öffnen Sie eine frühere Datenbankdatei über Ihren Dateimanager, um sie weiter zu verwenden. Ein Link zum Speicherort Ihrer Datei ist ausreichend Sie können Ihre Datenbank auch mit einem physischen Link öffnen (z.B. mit file:// und content://). - Neue Einträge zu Ihrer Datenbank hinzufügen - Elemente hinzufügen um Ihre digitalen Identitäten zu verwalten.\n\nGruppen hinzufügen (wie Ordner) um Ihre Einträge und Datenbank zu ordnen. - Ihre Einträge ganz einfach durchsuchen - Einträge nach Titel, Benutzernamen oder anderen Feldern durchsuchen, um ganz einfach Ihre Passwörter wiederzufinden. - Ihre Datenbank mit Ihrem Fingerabdruck entsperren - Verknüpfen Sie Ihr Passwort und Ihren Fingerabdruck um Ihre Datenbank im Handumdrehen zu entsperren. + Einträge zu Ihrer Datenbank hinzufügen + Einträge um Ihre digitalen Identitäten zu verwalten. +\n +\nGruppen (wie Ordner) um Einträge in ihrer Datenbank zu ordnen. + Einträge durchsuchen + Titel, Benutzernamen oder Inhalte anderer Feldern eingeben um Ihre Passwörter wiederzufinden. + Datenbank mit Fingerabdruck entsperren + Verknüpfen Sie Ihr Passwort und Ihren Fingerabdruck um Ihre Datenbank schnell zu entsperren. Eintrag bearbeiten Bearbeiten Sie Ihren Eintrag mit eigenen Feldern, Sie können zwischen Feldern aus verschiedenen Einträgen Verweise hinzufügen, um Daten zusammenzufassen. - Ein starkes Passwort erstellen + Ein starkes Passwort für ihren Eintrag erstellen. Generieren Sie ein starkes Passwort zu Ihrem Eintrag, stellen Sie Ihre Kriterien zur Erstellung des Passworts nach den gewünschten Merkmalen ein und vergessen Sie nie wieder ein schweres, aber sicheres Passwort. Benutzerdefinierte Felder hinzufügen - Wenn Sie ein eigenes, hier nicht aufgelistetes Feld eintragen möchten, erstellen Sie hier ein neues, welches Sie auch visuell schützen können. + Registrieren Sie ein einfaches, hier nicht aufgelistetes Feld indem sie ein neues hinzufügen, welches Sie auch schützen können. Ihre Datenbank entsperren Ein Feld kopieren Kopieren Sie einfach ein Feld, um es, wo Sie wollen, wieder einzufügen @@ -317,12 +319,12 @@ \nSie können verschiedene Methoden zum Ausfüllen von Formularen verwenden. Wählen Sie die, die Ihnen am meisten zusagt. Datenbank sperren Schnell Ihre Datenbank sperren, in den App-Einstellungen können Sie eine Sperre nach Zeit und bei Ausschalten des Bildschirms festlegen. - Einträge sortieren - Einträge und Gruppen nach spezifischen Parametern sortieren. + Eintrag-Sortierung + Auswählen, wie Einträge und Gruppen sortiert werden. Mitmachen Machen Sie mit, damit sich Stabilität, Sicherheit verbessern und weitere Funktionen hinzukommen. - Anders als viele andere Passwortmanager ist dieser werbefrei, quelloffen und speichert selbst in der kostenlosen Version keine persönlichen Daten auf Servern. + Anders als viele andere Passwortmanager ist dieser werbefrei, quelloffen und speichert keine persönlichen Daten auf dessen Servern - unabhängig von der Version, die Sie nutzen. Mit dem Kauf der Pro-Version erhalten Sie Zugang zu dieser visuellen Funktion und Sie unterstützen insbesondere die Umsetzung gemeinschaftlicher Projektarbeiten. Diese visuelle Funktion wurde wegen Ihrer Großzügigkeit freigeschaltet. Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren Beitrag. @@ -333,7 +335,7 @@ bestärken Sie die Entwickler, neue Funktionen einzuführen und gemäß Ihren Anmerkungen Fehler auszumerzen. Vielen Dank für Ihre Unterstützung. Wir bemühen uns, diese Funktion bald zu veröffentlichen. - Vergessen Sie nicht, Ihre App aktuell zu halten. + Vergessen Sie nicht, Ihre App aktuell zu halten indem sie neue Versionen installieren. Download Unterstützen @@ -343,8 +345,8 @@ AES KDF Argon2 - Icon-Paket auswählen - Das Symbolpaket der App ändern + Icon-Paket + In der App verwendetes Icon-Packet Eine Gruppe kann nicht in sich selbst verschoben werden. Kopieren Verschieben @@ -375,11 +377,10 @@ Schreibschutz aktivieren Datenbank standardmäßig schreibgeschützt öffnen - Ändern Sie den Modus bei Eröffnung einer Sitzung. -\n -\nIm schreibgeschützten Modus verhindern Sie unbeabsichtigte Änderungen an der Datenbank. -\n -\nIm Modus ‘Lesen und Schreiben‘ können Sie alle Elemente hinzufügen, löschen oder verändern, wie Sie möchten. + Ändern Sie den Öffnungs-Modus dieser Sitzung. +\n +\nIm schreibgeschützten Modus verhindern Sie unbeabsichtigte Änderungen an der Datenbank. +\nIm Modus ‘Lesen und Schreiben‘ können Sie alle Elemente hinzufügen, löschen oder verändern. Eintrag bearbeiten Datenbank kann nicht geladen werden. Laden des Schlüssels fehlgeschlagen; versuchen Sie, die \"Speicherplatznutzung\" von KDF zu verringen. From fa1c49aaf27df91c310b653f2bbb3b4930045f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 7 Nov 2018 05:55:45 +0000 Subject: [PATCH 011/289] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 99.1% (342 of 345 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index ad3ffb5c9..188f1a507 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -226,7 +226,7 @@ Filnavn Sti Tildel en hovednøkkel - Oppret en KeePass-fil + Opprett ny KeePass-fil Byte Filsti Vis hele filstien From 1424001ffd698aac3616f4b35395df69fd626cba Mon Sep 17 00:00:00 2001 From: TasogareRiiku Date: Wed, 7 Nov 2018 18:47:27 +0000 Subject: [PATCH 012/289] Translated using Weblate (Portuguese (Brazil)) Currently translated at 41.7% (144 of 345 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 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index d9bc3e1f8..1aa38fc8d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -21,13 +21,13 @@ --> - Comentários: + Comentário: Página inicial: - KeePass DX é uma implementação do gerenciador de senhas KeePass. + KeePass DX é uma implementação para Android do gerenciador de senhas KeePass Aceitar Adicionar entrada Adicionar grupo - Algoritmo + Algoritmo de Encriptação Tempo limite para o aplicativo Tempo até que o banco de dados seja travado quando o aplicativo estiver inativo. Aplicação From 20553713a8b267b106f42227368ccca741f3cb0d Mon Sep 17 00:00:00 2001 From: MESUT AKCAN Date: Sun, 11 Nov 2018 13:14:56 +0000 Subject: [PATCH 013/289] Added translation using Weblate (Turkish) --- app/src/main/res/values-tr/strings.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app/src/main/res/values-tr/strings.xml diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml new file mode 100644 index 000000000..a6b3daec9 --- /dev/null +++ b/app/src/main/res/values-tr/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From 948b54dc588db049d6321ed9e3d2cd4c82bf5815 Mon Sep 17 00:00:00 2001 From: xanadus1 Date: Sun, 11 Nov 2018 07:41:31 +0000 Subject: [PATCH 014/289] Translated using Weblate (Chinese (Simplified)) Currently translated at 37.4% (129 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7652c0e77..e1120ec26 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -18,9 +18,9 @@ along with KeePass DX. If not, see . --> - 反馈: - 主页: - KeePass DX 是 KeePass 密码管理器在Android 平台上的一个实现。 + 反馈 + 主页 + KeePass 密码管理器在 Android 平台上的实现 接受 添加条目 添加文件夹 From 9b7b363b494b2c2cc3733a815232eaeff9b69812 Mon Sep 17 00:00:00 2001 From: wellinkstein Date: Sun, 11 Nov 2018 09:27:38 +0000 Subject: [PATCH 015/289] Translated using Weblate (French) Currently translated at 98.0% (338 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 285 ++++++++++++++----------- 1 file changed, 156 insertions(+), 129 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 70273b683..f81ce7fe5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -36,15 +36,15 @@ Ne plus afficher Crochets ASCII étendu - La navigation dans les fichiers nécessite OpenIntents File Manager, cliquez ci-dessous pour l\'installer. En raison de certaines bizarreries dans le gestionnaire de fichiers, la navigation peut ne pas fonctionner correctement la première fois que vous naviguez. + Parcourez les fichiers en installant le gestionnaire de fichiers OpenIntents Annuler Permettre Presse-papier vidé Erreur de presse-papier - Certains appareils Android Samsung ont un bug dans l\'implémentation du presse-papier qui empêche la copie depuis des applications. Plus de détails : - Le vidage du presse-papier a échoué + Certains appareils Android Samsung ne permettent pas aux applications d\'utiliser le presse-papier. + Impossible d\'effacer le presse-papier Expiration du presse-papier - Temps avant le vidage du presse-papier après copie du nom d\'utilisateur ou du mot de passe + Durée de stockage dans le presse-papier Balayez pour effacer le presse-papiers maintenant Copier %1$s dans le presse-papier Création de la clé de base de données… @@ -52,59 +52,59 @@ Déchiffrement du contenu de la base de données… Utiliser comme base de données par défaut Nombres - KeePass DX \u00A9 %1$d Kunzisoft n\'offre ABSOLUMENT AUCUNE GARANTIE; il s\'agit d\'un logiciel libre, vous pouvez le redistribuer sous les conditions de la licence GPL v3 ou ultérieure. + KeePass DX © %1$d Kunzisoft n\'offre ABSOLUMENT AUCUNE GARANTIE ; il s\'agit d\'un logiciel libre, vous pouvez le redistribuer sous les conditions de la licence GPL v3 ou ultérieure. Dernier accès Annuler - Commentaires + Notes Confirmer mot de passe Créé Expire Fichier clé Modifié - Données d\'entrée non trouvées. + Impossible de trouver les données d\'entrée. Mot de passe Enregistrer Titre URL Nom d\'utilisateur Le chiffrement de flux ARCFOUR n\'est pas supporté. - KeePass DX ne peut pas gérer cette URI. + Impossible de gérer cette URI dans KeePass DX. La création du groupe parent a échoué. Le fichier existe déjà. Échec d\'ouverture du lien. - Le nom de fichier est obligatoire. + Saisissez un nom de fichier. Impossible de créer le fichier : - Base de données invalide ou clé maitresse non reconnue. - Chemin invalide. - Le nom est obligatoire. - Un fichier clé est requis. - L\'appareil ne dispose pas de suffisamment de mémoire pour ouvrir votre base de données. - Au moins un type de génération de mot de passe doit être sélectionné + Impossible de lire la base de données. + Assurez-vous de la validité du chemin. + Saissisez un nom. + Sélectionnez un fichier clé. + Pas de mémoire pour charger l\'ensemble de votre base de données. + Au moins un type de génération de mot de passe doit être sélectionné. Les mots de passe ne correspondent pas. \"Niveaux\" doit être un nombre. \"Niveaux\" trop gros. Paramètres à 2147483648. Un nom de champ est requis pour chaque élément. - Le titre est obligatoire. - Entrez un entier positif pour le champ \"Longueur\" - Le service de remplissage automatique ne peut être activé. + Ajoutez un titre. + Entrez un entier positif dans le champ \"Longueur\". + Impossible d\'activer le service de remplissage automatique. Nom du champ - Valeur - Fichier non trouvé. - Fichier non trouvé. Essayer de l\'ouvrir à nouveau à partir de votre fournisseur de contenu. + Valeur du champ + Impossible de trouver le fichier. + Impossible de trouver le fichier. Essayez de l\'ouvrir à nouveau à partir de votre navigateur de fichiers. Navigateur de fichiers Générer mot de passe confirmez mot de passe mot de passe généré Nom de groupe - clé de fichier + Fichier clé longueur mot de passe Mot de passe Installer depuis Google Play Installer depuis F-Droid Mot de passe ou fichier de clé invalide. - Algorithme invalide. - Format de base de données non reconnu. + Mauvais algorithme. + Impossible de reconnaître le format de la base de données. Le fichier de clé n\'existe pas. Le fichier de clé est vide. Longueur @@ -112,8 +112,8 @@ Taille de la police de caractères utilisée pour les éléments de liste Ouverture de la base de données… Minuscule - Afficher le mot de passe - Par défaut, masquer le mot de passe + Masquer les mots de passe + Masquer les mots de passe (***) par défaut À propos Modifier la clé maître Copie de %1$s @@ -127,34 +127,34 @@ Ouvrir Rechercher Afficher le mot de passe - Supprimer la clé de l\'empreinte + Supprimer l\'empreinte digitale enregistrée Ouvrir l\'URL Moins Jamais Aucun résultat pour cette recherche - Impossible d\'ouvrir cette URL. - Sélectionner une base de données existante - Bases de données récentes : + Installez un navigateur web pour ouvrir cette URL. + Ouvvrir une base de données existante + Bases de données récentes Ne pas rechercher dans les entrées de sauvegarde - Ignorer le groupe \'Sauvegardes\' des résultats de recherche (uniquement pour .kdb) + Omet le groupe \"Sauvegarde\" des résultats de recherche (ne s\'applique qu\'aux fichiers .kdb) Création d\'une nouvelle base de données… Veuillez patienter… Protection - Lecture seule - KeePass DX n\'a pas la permission d\'écrire dans votre répertoire de base de données, celle-ci sera ouverte en lecture seule. + Protégé en écriture + KeePass DX a besoin d\'une autorisation d\'écriture pour pouvoir modifier quoi que ce soit dans votre base de données. À partir d\'Android KitKat, certains appareils n\'autorisent plus les applications à écrire sur la carte SD. Historique de fichiers récents - Mémoriser les fichiers récemment utilisés - Mémoriser le répertoire de stockage des fichiers de clés + Mémoriser les noms de fichiers récents + Mémoriser l\'emplacement des fichiers clés des bases de données Mémoriser le fichier de clé Effacer Racine - Algorithme pour chiffrer toute la base de données. (Les mots de passe, noms d\'utilisateur, notes et toutes les données dans la base de données sont chiffrés avec l\'algorithme sélectionné) - Afin de générer la clé pour l\'algorithme de chiffrement, la clé maîtresse compressée (SHA-256) est transformée en utilisant une fonction de dérivation de clé avec salage aléatoire. + Algorithme de chiffrement de la base de données utilisé pour toutes les données. + Afin de générer la clé pour l\'algorithme de chiffrement, la clé maîtresse est transformée en utilisant une fonction de dérivation de clé salée aléatoirement. Tours de transformation Un niveau de chiffrement supérieur assure une protection supplémentaire contre les attaques de force brute, mais peut considérablement ralentir l\'ouverture et l\'enregistrement. niveaux - Utilisation de la Mémoire + Utilisation de la mémoire Quantité de mémoire (en octets binaires) à utiliser par la fonction de dérivation de clé. Parallélisme Degré de parallélisme (nombre de threads) utilisé par la fonction de dérivation de clé. @@ -168,122 +168,124 @@ Tri par défaut (base de données) Titre Nom d\'utilisateur - Date de Création - Date de Dernière Modification - Date de Dernier Accès + Création + Modification + Accès Spécial Nom/description Résultats Souligné Version de la base de données non supportée. Majuscule - Utiliser l\'Environnement d\'Accès au Stockage Android pour naviguer dans les fichiers (KitKat ou plus récent) - Environnement d\'Accès au Stockage + Utiliser l\'environnement d\'accès au stockage (SAF) Android pour naviguer dans les fichiers (KitKat ou plus récent) + Environnement d\'accès au stockage Alerte - Votre mot de passe peut contenir des lettres non prises en charge par le jeu de caractères Latin-1 utilisé par les fichiers .kdb. Comme elles sont toutes converties vers la même lettre, il est recommandé de changer votre mot de passe pour le rendre plus sûr. - Votre carte SD est actuellement en lecture seule. Vous ne pourrez pas enregistrer les changements dans la base de données. - Votre carte SD n\'est actuellement pas montée sur votre appareil. Vous ne pourrez pas charger ou créer votre base de données. - Voulez-vous vraiment utiliser une chaîne de caractères vide comme mot de passe ? + Évitez les caractères de mot de passe en dehors de Latin-1 dans les fichiers .kbd car ils sont tous convertis dans la même lettre. + Accordez l\'accès en écriture à la carte SD pour enregistrer les modifications apportées à la base de données. + Montez la carte SD pour créer ou charger une base de données. + Voulez-vous vraiment pas de protection de déverouillage par mot de passe \? Êtes-vous sûr de ne vouloir utiliser aucune clé de chiffrement ? Version %1$s - La reconnaissance d\'empreinte digitale est supportée mais non configuré pour cet appareil - Attente d\'une reconnaissance d\'empreinte digitale + La reconnaissance d\'empreinte digitale est supportée mais non configurée. + Reconnaissance d\'empreinte digitale Mot de passe encrypté stocké - Problème de clé invalide. Restaurez votre mot de passe. - Empreinte digitale non reconnue - Problème d\'empreinte digitale : %1$s + Impossible de lire les empreintes digitales. Restaurez votre mot de passe. + Impossible de reconnaître les empreintes digitales + Problème d\'empreintes digitales : %1$s Utiliser l\'empreinte digitale pour stocker le mot de passe - Pas de mot de passe encore stocké pour cette base de données + Cette base de données n\'a pas encore de mot de passe. Historique Apparence Générale Remplissage automatique - Service de saisie KeePass DX + Remplissage automatique des formulaires KeePass DX Se connecter avec KeePass DX - Définir le service de saisie automatique par défaut - Activer le service qui permet de remplir facilement les formulaires depuis d\'autres applications - Taille de mot de passe - Définir la taille par défaut du mot de passe généré + Définir le service de remplissage automatique par défaut + Activer le remplissage automatique pour remplir rapidement des formulaires dans d\'autres applications + Taille du mot de passe généré + Définir la taille par défaut des mots de passe générés Caractères de mot de passe - Définir les caractères par défaut du générateur de mot de passe + Définir les caractères autorisés du générateur de mot de passe Presse-papiers Notifications du presse-papiers Activer les notifications du presse-papiers pour copier les champs d\'entrées - Si votre appareil ne parvient pas à supprimer automatiquement les éléments du presse-papiers, supprimez l\'élément copié de l\'historique de votre presse-papiers manuellement. + Si la suppression automatique du presse-papier échoue, supprimez son historique manuellement. Verrouiller Verrouillage d\'écran Verrouiller la base de données quand l\'écran est éteint Comment configurer l\'empreinte digitale pour un déverrouillage rapide ? - Définissez votre empreinte digitale personnelle pour votre appareil dans + Enregistrez votre empreinte digitale pour votre appareil dans \"Paramètres\" → \"Sécurité\" → \"Empreinte digitale\" - Tapez votre mot de passe dans Keepass DX - Scannez votre empreinte digitale pour stocker votre mot de passe maître en toute sécurité - Scanner votre empreinte digitale lorsque la case à cocher du mot de passe n\'est pas cochée pour ouvrir la base de données + Entrez le mot de passe de verrouillage de la base de données + Scannez votre empreinte digitale pour stocker le mot de passe de votre base de données en toute sécurité. + Scannez votre empreinte digitale pour ouvrir la base de données lorsque le mot de passe est désactivé. Usage Empreinte digitale - Scan d\'empreintes digitales - Activer l\'ouverture de la base de données par empreinte digitale + Reconnaissance d\'empreintes digitales + Vous permet de scanner vos empreintes digitales pour ouvrir la base de données Supprimer les clés de chiffrement Supprimer toutes les clés de chiffrement liées à la reconnaissance des empreintes digitales - Êtes-vous sûr de vouloir supprimer toutes les clés liées aux empreintes digitales ? + Êtes-vous sûr de vouloir supprimer toutes les clés relatives à la reconnaissance d\'empreintes digitales \? Impossible de démarrer cette fonctionnalité. Votre version Android %1$s n\'est pas la version minimale %2$s requise. - Le matériel n\'est pas détecté. + Impossible de trouver le matériel correspondant. Nom de fichier Chemin Assigner une clé maître - Créer un fichier KeePass + Créer une nouvelle base de données Octets Chemin de fichier Afficher le chemin complet des fichiers Utiliser la corbeille - Déplace un groupe ou une entrée dans la \"Corbeille\" avant la suppression - KeePass DX a besoin d\'une permission de stockage externe pour écrire une base de données - KeePass DX a besoin d\'une permission de stockage externe pour lire une URI non fournie par un content provider - Permission de stockage externe refusée - Impossible d\'effectuer l\'action sans autorisation de stockage externe - Police de champs - Changer la police de caractères des champs pour une meilleure visibilité - Ouvrir le fichier sélectionné automatiquement - Ouvrir automatiquement un fichier à partir de l\'écran de sélection après une sélection dans le navigateur de fichiers - Copie de mot de passe - Autoriser la copie du mot de passe et des champs protégés dans le presse-papiers + Déplace les groupes et entrées dans la \"Corbeille\" avant la suppression + KeePass DX a besoin d\'une permission de stockage externe pour écrire dans une base de données. + KeePass DX a besoin d\'une permission de stockage externe pour lire une URI non fournie par un fournisseur de contenu. + Impossible d\'obtenir la permission de stockage externe. + Impossible d\'effectuer l\'action sans permission de stockage externe. + Police de champ + Changer la police utilisée dans les champs pour une meilleure visibilité des caractères + Ouvrir les fichiers en sélectionnant + Ouvrir automatiquement les fichiers lorsqu\'ils sont sélectionnés dans le navigateur de fichiers + Confiance dans le presse-papier + Autoriser le mot de passe de l\'entrée et les champs protégées à entrer dans le presse-papier ATTENTION : Le presse-papiers est partagé par toutes les applications. Si des données sensibles sont copiés, d\'autres logiciels peuvent les récupérer. - ATTENTION : désactiver cette fonctionnalité peut engendrer une impossibilité d\'ouvrir ou sauvegarder les bases de données - Lien du fichier KDBX à ouvrir + ATTENTION : La désactivation de cette fonction peut empêcher l\'ouverture ou la sauvegarde des bases de données. + Lien du fichier de base de données à ouvrir Nom de la base de données Description de la base de données Version de la base de données - Apparence de texte - Apparence de l\'application + Texte + Application Autres Clavier Magikeyboard - Activer un clavier customisé qui permet le renseignement de vos mots de passe et de tous vos champs d\'identité facilement + Activer un clavier personnalisé pour remplir vos mots de passe et tous les champs d\'identité - Ecrans d\'éducation + Ecrans éducatifs Met en surbrillance les éléments pour apprendre le fonctionnement de l\'application - Réinitialiser les écrans d\'éducation - Réinitialise l\'affichage des éléments d\'éducation - Ecrans d\'éducation réinitialisés + Réinitialiser les écrans éducatifs + Montrer à nouveau tous les éléments éducatifs + Ecrans éducatifs réinitialisés Créez votre fichier de base de données - Vous ne connaissez pas encore KeePass DX, créez votre premier fichier de gestion de mots de passe. + Créez votre premier fichier de gestion de mot de passe. Ouvrez une base de données existante - Vous avez déjà utilisé un lecteur KeePass. Ouvrez simplement votre fichier KDBX depuis votre navigateur de fichiers. + Ouvrez votre ancien fichier de base de données à partir de votre navigateur de fichiers pour continuer à l\'utiliser. Un lien vers l\'emplacement de votre fichier suffit Vous pouvez aussi ouvrir votre base avec un lien physique. (avec file:// et content:// par exemple). - Ajoutez de nouveaux éléments à votre base - Ajoutez des entrées pour gérer vos identités numériques.\n\nAjoutez des groupes (l\'équivalent des dossiers) pour organiser vos entrées et votre base. - Recherchez facilement vos entrées - Recherchez des entrées par titre, nom d\'utilisateur ou par d\'autres champs pour récupérer facilement vos mots de passes. - Débloquez votre base de données avec votre empreinte digitale - Faites le lien entre votre mot de passe et votre empreinte digitale pour facilement dévérouiller votre base de données. + Ajoutez des éléments à votre base de données + Les entrées vous aident à gérer vos identités numériques. +\n +\nLes groupes (~dossiers) organisent les entrées dans votre base de données. + Recherchez dans les entrées + Entrez le titre, le nom d\'utilisateur ou le contenu des autres champs pour retrouver vos mots de passe. + Déverouillage de la base de données par empreintes digitales + Associez votre mot de passe à vos empreintes digitales scannées pour déverouiller rapidement votre base de données. Editez l\'entrée - Éditez votre entrée avec des champs customisés, des références pour mutualiser les données peuvent être ajoutées entre les champs de différentes entrées. - Créez un mot de passe fort - Générez un mot de passe fort à associer avec votre entrée, définissez le facilement en fonction des critères du formulaire et n\'oubliez plus de mot de passe sécurisé . + Éditez votre entrée avec des champs personnalisés. La collection de données peut être référencée entre différents champs de l\'entrée. + Créez un mot de passe fort pour votre entrée. + Générez un mot de passe fort à associer avec votre entrée, définissez le facilement en fonction des critères du formulaire et n\'oubliez pas le mot de passe sécurisé. Ajoutez des champs customisés - Vous voulez enregistrer un champ non fourni de base, renseignez en simplement un nouveau que vous pouvez aussi protéger visuelement. + Enregistrez un champ de base non fourni en remplissant un nouveau champ que vous pouvez égalemement protéger. Débloquez votre base de données Entrez un mot de passe et/ou un fichier de clé pour ouvrir la base de données. \n @@ -294,15 +296,15 @@ \n Vous pouvez utilisez plusieurs méthodes de remplissage de formulaire. Utilisez celle que vous préférez. Vérouillez la base de données Verrouillez votre base de données rapidement, vous pouvez paramétrer l\'application pour qu\'elle se verrouille après un certain temps et lorsque l\'écran s\'éteint. - Triez les éléments - Triez les entrées et les groupes en fonction de paramètres spécifiques. + Tri des éléments + Choississez comment les entrées et les groupes sont triés. Participez - Participer pour aider à augmenter la stabilité, la sécurité et ajouter plus de fonctionnalités. + Aidez à améliorer la stabilité, la sécurité et à ajouter plus de fonctionnalités. - Contrairement à d\'autres applications de gestion de mots de passe, cette application est sans publicité, open source sous licence copyleft et ne récupère pas de données personnelles sur ses serveurs, même dans sa version gratuite. + Contrairement à d\'autres applications de gestion de mots de passe, cette application est sans publicité, libre sous licence copyleft et ne collecte pas de données personnelles sur ses serveurs, peu importe la version que vous utilisez. En achetant la version pro, vous accéderez à cette <strong>fonctionnalité visuelle</strong> et vous aiderez en particulier à <strong>la réalisation de projets communautaires.</strong> Cette <strong>fonctionnalité visuelle</strong> est disponible grâce à votre générosité. - Afin de garder notre liberté et d\'être toujours actif, nous comptons sur votre <strong>contribution.</strong> + Afin de garder notre liberté et de toujours être actifs, nous comptons sur votre contribution. Cette fonctionnalité est <strong>en cours de développement</strong> et nécessite votre <strong>contribution</strong> pour être prochainement disponible. En achetant la version <strong>pro</strong>, @@ -310,7 +312,7 @@ vous encouragez les développeurs à créer de <strong>nouvelles fonctionnalités</strong> et à <strong>résoudre des bugs</strong> en fonction de vos remarques. Merci beaucoup pour votre contribution. Nous travaillons dur pour sortir cette fonctionnalité rapidement. - N\'oubliez pas de garder votre application à jour. + N\'oubliez pas de garder votre application à jour en installant les nouvelles versions. Télécharger Contribuer @@ -341,8 +343,8 @@ Grand - Choisir un thème - Changer le thème de l\'application en modifiant les couleurs + Thème de l\'application + Thème utilisé dans l\'application Thème Jour Thème Nuit @@ -351,43 +353,68 @@ Thème Rouge Volcan Thème Pro Violet - Choisir un pack d\'icones - Changer le pack d\'icones de l\'application + Pack d\'icônes + Pack d\'icônes utilisé dans l\'application -Impossible de déplacer un groupe en lui-même. +Vous ne pouvez pas déplacer un groupe en lui-même. Copier Déplacer Coller Annuler Paramètres du Magikeyboard - Comment configurer le clavier pour le remplissage sécurisé de formulaire ? - Activer le Magikeyboard dans les paramètres de l\'appareil. + Configurer le clavier pour le remplissagee automatique des formulaires en toute sécurité. + Activez le \"Magikeyboard\" dans les paramètres de l\'appareil. \"Paramètres\" → \"Langue & saisie\" → \"Clavier actuel\" et en choisir un. ou (\"Paramètres\" → \"Système\" → \"Langues et saisie\" → \"Clavier virtuel\" et en choisir un.) Choisir le Magikeyboard lorsque vous avez besoin de remplir un formulaire. - Vous pouvez facilement passer de votre clavier principal à Magikeyboard avec le bouton langage de votre clavier, un appui long sur la barre d\'espace de votre clavier, ou, si ce n\'est pas disponible, avec : + Changez de clavier en appuyant longuement sur la barre espace de votre clavier, ou, si ce n\'est pas disponible, avec : Sélectionnez votre entrée avec la clé. Remplissez vos champs avec les éléments de l\'entrée. Verrouiller la base de données. - Retourner sur votre clavier principal. + Utiliser à nouveau le clavier par défaut. Autoriser aucun mot de passe - Activer le bouton d\'ouverture si aucune identification de mot de passe n\'est sélectionnée + Activer le bouton \"ouvrir\" si aucune identification de mot de passe n\'est sélectionnée - Lecture seule - Lecture et écriture - Lecture seule - Par défaut, ouvrir une base de données en mode lecture seule + Protégé en écriture + Modifiable + Protégé en écriture + Ouvrir votre base de données en lecture seule par défaut - Activer la lecture seule - Changez le mode d\'ouverture de la session. -\n -\nEn mode lecture seule, vous empêchez les modifications involontaires de la base de données. -\n -\nEn mode écriture, vous pouvez ajouter, supprimer ou modifier tous les éléments comme vous le souhaitez. + Protégez en écriture votre base de données + Changez le mode d\'ouverture de la session. +\n +\n\"Protégé en écriture\" empêche les modifications involontaires de la base de données. +\n +\n\"Modifiable\" vous permet d\'ajouter, supprimer ou modifier tous les éléments. Modifier l\'entrée - La base de données ne peut être chargée - Impossible de charger la clé, essayez de diminuer la mémoire utilisée par le KDF. + Impossible de chager votre base de données. + Impossible de charger la clé. Essayez de diminuer la mémoire utilisée par la KDF. Afficher les noms d\'utilisateur Afficher les noms d\'utilisateur dans les listes d\'entrées + Construire %1$s + Magikeyboard + Magikeyboard (KeePass DX) + Paramètres Magikeyboard + + Entrée + + Délai d\'attente + Délai d\'attente pour effacer l\'entrée au clavier + + Informations de notification + Afficher une notification lorsqu\'une entrée est disponible + Entrée + 1$s disponible sur Magikeyboard + %1$s + + Effacer à la fermeture + Effacer l\'entrée du clavier lors de la fermeture de la notification + Apparence + + Thème du clavier + + Touches + Vibrer au toucher + Son au toucher From ac71aec39d2593d4c3fdede6bb48ce8a1115e676 Mon Sep 17 00:00:00 2001 From: MESUT AKCAN Date: Sun, 11 Nov 2018 13:17:48 +0000 Subject: [PATCH 016/289] Translated using Weblate (Turkish) Currently translated at 13.6% (47 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 51 +++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index a6b3daec9..0ce3004f3 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,2 +1,49 @@ - - \ No newline at end of file + +Geri Bildirim + Ana sayfa + KeePass parola yöneticisinin Android uygulaması + Kabul et + "Girdi Ekle " + "Girdi Düzenle " + "Grup Ekle " + Dizi ekle + Şifreleme + Şifreleme algoritması + Anahtar üretme fonksiyonu + Uygulama zaman aşımı + Uygulama kilitlemeden önce durgunluk + Uygulama + Tekrar gösterme + Parantez + Genişletilmiş ASCII + OpenIntents Dosya Yöneticisi\'ni yükleyerek dosyalara göz atın + İptal + İzin ver + Pano temizlendi + Pano hatası + Bazı Samsung Android telefonları, uygulamaların panoya kullanılmasına izin vermez. + "Pano temizlenemedi " + Pano zaman aşımı + Panodaki depolama süresi + Veritabanı + Erişildi + İptal + Notlar + Parolayı onayla + Oluşturuldu + Değiştirilmiş + Giriş Verisi bulamadı. + Parola + Kaydet + Başlık + URL + Kullanıcı adı + Ana dizin oluşturulamadı. + Bu dosya zaten var. + Bağlantı açılamadı. + Bir dosya adı girin. + Dosya oluşturulamadı: + Veritabanı okunamadı. + Yolun doğru olduğundan emin olun. + Bir isim girin. + From af8ca15e29d34168d35ac04f850ba7a5fefb04c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=2E=20R=C3=BCdinger?= Date: Wed, 14 Nov 2018 10:45:13 +0000 Subject: [PATCH 017/289] Translated using Weblate (German) Currently translated at 98.6% (340 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1ebc62cc7..d8efaa4c1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -212,7 +212,7 @@ Sperre Erlaubte Zeichen für Passwortgenerator festlegen Passwortzeichen - Fingerabdruck-Scannen wird unterstützt, ist jedoch nicht konfiguriert + Fingerabdruck-Scannen wird unterstützt, ist jedoch nicht konfiguriert. Fingerabdruck wird gescannt Verschlüsseltes Passwort wurde gespeichert Fingerabdruckschlüssel nicht lesbar. Stellen Sie Ihr Passwort wieder her. @@ -298,7 +298,7 @@ Öffnen Sie eine frühere Datenbankdatei über Ihren Dateimanager, um sie weiter zu verwenden. Ein Link zum Speicherort Ihrer Datei ist ausreichend Sie können Ihre Datenbank auch mit einem physischen Link öffnen (z.B. mit file:// und content://). - Einträge zu Ihrer Datenbank hinzufügen + Datenbankelemente hinzufügen Einträge um Ihre digitalen Identitäten zu verwalten. \n \nGruppen (wie Ordner) um Einträge in ihrer Datenbank zu ordnen. @@ -311,7 +311,7 @@ Ein starkes Passwort für ihren Eintrag erstellen. Generieren Sie ein starkes Passwort zu Ihrem Eintrag, stellen Sie Ihre Kriterien zur Erstellung des Passworts nach den gewünschten Merkmalen ein und vergessen Sie nie wieder ein schweres, aber sicheres Passwort. Benutzerdefinierte Felder hinzufügen - Registrieren Sie ein einfaches, hier nicht aufgelistetes Feld indem sie ein neues hinzufügen, welches Sie auch schützen können. + Registrieren Sie ein zusätzliches Feld und tragen Sie einen Wert dafür ein, den Sie wahlweise schützen können. Ihre Datenbank entsperren Ein Feld kopieren Kopieren Sie einfach ein Feld, um es, wo Sie wollen, wieder einzufügen @@ -319,7 +319,7 @@ \nSie können verschiedene Methoden zum Ausfüllen von Formularen verwenden. Wählen Sie die, die Ihnen am meisten zusagt. Datenbank sperren Schnell Ihre Datenbank sperren, in den App-Einstellungen können Sie eine Sperre nach Zeit und bei Ausschalten des Bildschirms festlegen. - Eintrag-Sortierung + Sortierung der Elemente Auswählen, wie Einträge und Gruppen sortiert werden. Mitmachen Machen Sie mit, damit sich Stabilität, Sicherheit verbessern und weitere Funktionen hinzukommen. @@ -377,10 +377,10 @@ Schreibschutz aktivieren Datenbank standardmäßig schreibgeschützt öffnen - Ändern Sie den Öffnungs-Modus dieser Sitzung. + Ändern Sie den Modus bei Eröffnung der Sitzung. \n -\nIm schreibgeschützten Modus verhindern Sie unbeabsichtigte Änderungen an der Datenbank. -\nIm Modus ‘Lesen und Schreiben‘ können Sie alle Elemente hinzufügen, löschen oder verändern. +\n\"Schreibgeschützt\" verhindert unbeabsichtigte Änderungen an der Datenbank. +\n\"Veränderbar\" ermöglicht das Hinzufügen, Löschen oder Bearbeiten von Elementen. Eintrag bearbeiten Datenbank kann nicht geladen werden. Laden des Schlüssels fehlgeschlagen; versuchen Sie, die \"Speicherplatznutzung\" von KDF zu verringen. From 214dca1d4ebf6591149fb4965857dd19247753a5 Mon Sep 17 00:00:00 2001 From: GoodMirek Date: Fri, 16 Nov 2018 14:15:24 +0000 Subject: [PATCH 018/289] Translated using Weblate (Czech) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/ --- app/src/main/res/values-cs/strings.xml | 263 ++++++++++++++----------- 1 file changed, 144 insertions(+), 119 deletions(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 8e180628d..921d9397b 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -21,35 +21,35 @@ --> - Připomínky: - Domovská stránka: - KeePass DX je Android implementace správce hesel KeePass. + Nahlášení problému + Domovská stránka + Androidová verze správce hesel KeePass Přijmout Přidat záznam Přidat skupinu Šifrovací algoritmus Časový limit aplikace - Doba nečinnosti v aplikaci, po které uzamknout databázi + Doba nečinnosti, po které se aplikace zamkne Aplikace Nastavení aplikace Znovu nezobrazovat Závorky - "Procházení souborů využívá správce souborů Open Intents – ten nainstalujete ťuknutím níže. Kvůli zvláštnostem ve správci souborů se může stát, že procházení při prvním spuštění nezafunguje správně." + Instalace správce souborů OpenIntents k procházení souborů Storno Schránka vyčištěna Chyba schránky - Některé Android telefony od Samsung mají chybu v implementaci schránky, která způsobuje chybu při kopírování z aplikací. Další podrobnosti naleznete na: - Vyčištění schránky se nezdařilo + Některé Android telefony od Samsungu nedovolují aplikacím používat schránku. + Nelze vyprázdnit schránku Časový limit schránky - Doba po zkopírování jména nebo heslo po které vyčistit chránku + Doba uchování ve schránce Vyberte zkopírovat %1$s do schránky Vytváření klíče databáze… Databáze - Rozšifrování obsahu databáze… - Použít tuto databázi jako výchozí + Rozšifrovávání obsahu databáze… + Použít jako výchozí databázi Číslice - KeePass DX \u00A9 %1$d Kunzisoft přichází BEZ JAKÉKOLIV ZÁRUKY; Toto je svobodný software a je možné ho šířit dál za dodržení podmínek licence GPL verze 3 nebo novější. - Vyberte existující databázi + KeePass DX © %1$d Kunzisoft je poskytován BEZ JAKÉKOLIV ZÁRUKY; Toto je svobodný software a je možné jej dále šířit za dodržení podmínek licence GPL verze 3 nebo novější. + Otevřít existující databázi Poslední přístup Storno Poznámky @@ -58,33 +58,33 @@ Platnost skončí Soubor s klíčem Změněno - Vstupní data nenalezena. + Data záznamu nenalezena. Heslo Uložit Název URL adresa Uživatelské jméno ARCFOUR proudová šifra není podporována. - KeePass DX se nedaří tuto URI zpracovat. + KeePass DX nemůže zpracovat toto URI. Nedaří se vytvořit nadřazenou složku. Tento soubor už existuje. Odkaz se nedaří otevřít. - Je vyžadován název souboru. + Vložte název souboru. Soubor se nedaří vytvořit: - Chybná databáze nebo nerozpoznaný hlavní klíč. - Neplatný popis umístění. - Je vyžadován název. - Je vyžadováno heslo nebo soubor s klíčem. - Přístroji došla při zpracovávání databáze kapacita operační paměti. - Je třeba zvolit alespoň jeden typ vytváření hesla + Nelze přečíst databázi. + Neplatná cesta. + Vložte jméno. + Vyberte soubor s klíčem. + Nedostatek paměti k otevření databáze. + Je třeba zvolit alespoň jeden způsob vytváření hesla. Zadání hesla se neshodují. - Je třeba, aby „Počet průchodů“ bylo číslo. - Příliš mnoho „průchodů“. Bude nastaveno na 2147483648. + „Počet průchodů“ musí být číslo. + Příliš vysoký „Počet průchodů“. Nastavuji na 2147483648. Je třeba, aby každý řetězec měl název kolonky. - Je vyžadován název. - Do kolonky „Délka“ zadejte celé kladné číslo - Název kolonky - Hodnota v kolonce + Přidejte název. + Do nastavení „Délka“ zadejte celé kladné číslo. + Název položky + Hodnota položky Soubor nenalezen. Správce souborů Vytvořit heslo @@ -107,8 +107,8 @@ Velikost textu v seznamu prvků Načítání databáze… Malá písmena - Skrýt heslo - Ve výchozím stavu skrývat heslo + Skrýt hesla + Ve výchozím stavu zobrazovat (***) místo hesla O aplikaci Změnit hlavní klíč Nastavení @@ -125,14 +125,14 @@ Mínus Nikdy Žádné výsledky hledání - Žádná obsluha pro tuto URL adresu. - Nedávné databáze: + Pro otevření tohoto URL nainstalujte webový prohlížeč. + Nedávno otevřené databáze Neprohledávat položky v záloze - Vynechat z výsledků vyhledávání skupinu „Záloha“ (platí pouze pro .kdb soubory) + Vynechat skupinu „Záloha“ z výsledků vyhledávání (platí pouze pro .kdb soubory) Vytváření nové databáze… Zpracování… Ochrana - KeePass DX nemá oprávnění pro zápis do místa uložení databáze, ta proto bude otevřena pouze pro čtení. + Ke změně v databázi potřebuje KeePass DX oprávnění pro zápis. S Android od verze KitKat neumožňují některá zařízení aplikacím zápis na SD kartu. Historie nedávných souborů Pamatovat si nedávno otevřené soubory @@ -143,11 +143,11 @@ Kořen Počet šifrovacích průchodů Vyšší počet šifrovacích průchodů zvýší odolnost proti útoku zkoušením všech možných hesel, ale může výrazně zpomalit načítání a ukládání. - průchody + šifrovací průchody Ukládání databáze… Místo Hledat - Pořadí řazení databáze + Obvyklé řazení databáze Speciální Hledat Výsledky hledání @@ -156,8 +156,8 @@ Nepodporovaná verze databáze. Velká písmena Verze %1$s - Vaše SD karta je nyní v režimu pouze pro čtení. Nebude možné ukládat změny v databázi. - Souborový systém na SD kartě momentálně není v systému zařízení připojený. Nebude možné načítat ani vytvářet databáze. + Povolte oprávnění k zápisu na SD kartu, aby bylo možné uložit změny v databázi. + Připojte SD kartu, aby bylo možné vytvářet a otevírat databáze. Databázi odemknete zadáním hesla a/nebo souboru s klíčem. \n \nNezapomeňte si po každé úpravě zazálohovat kopii svého .kdbx souboru na bezpečné místo. @@ -181,15 +181,15 @@ Upravit záznam Přidat řetězec Šifrování - Funkce pro odvozování klíče + Funkce pro tvorbu klíče Rozšířené ASCII Umožnit Schránku vymažete přejetím - Databázi se nedaří načíst - Klíč se nedaří načíst, zkuste snížit množství paměti, využívané funkcí pro odvození klíče. + Databázi se nedaří načíst. + Klíč se nedaří načíst, zkuste snížit množství paměti, využívané funkcí pro tvorbu klíče. Službu automatického vyplňování se nedaří zapnout. Není možné přesunout skupinu do ní samotné. - Soubor nenalezen. Zkuste ho znovu otevřít ze svého poskytovatele obsahu. + Soubor nenalezen. Zkuste jej otevřít ze správce souborů. Zobrazovat uživatelská jména V seznamech položek zobrazovat uživatelská jména Kopie %1$s @@ -202,146 +202,146 @@ Pouze pro čtení Čtení a zápis Pouze pro čtení - Algoritmus pro šifrování celé databáze. (Hesla, uživatelská jména, poznámky a všechna data v databázi jsou šifrována vybraným algoritmem) - Klíč pro šifrovací algoritmus je vytvořen přetvořením zkomprimovaného hlavního klíče (SHA-256) pomocí náhodně proložené funkce pro odvození klíče. + Algoritmus pro šifrování celé databáze. + Klíč pro šifrovací algoritmus je vytvořen transformací hlavního klíče s přidanou náhodnou složkou (tzv. solí) s použitím funkce pro tvorbu klíče. Využití paměti - Množství paměti (v binárních bajtech) které použít funkcí pro odvození klíče. + Množství paměti (v bajtech) použitých funkcí pro tvorbu klíče. Souběžné zpracovávání - Stupeň souběžného zpracovávání (tj. počet vláken) použitý funkcí pro odvození klíče. + Stupeň souběžného zpracovávání (počet vláken) použitý při tvorbě klíče. Seřadit Nejnižší první ↓ Skupiny první Koš jako poslední Nadpis Uživatelské jméno - Okamžik vytvoření - Okamžik poslední úpravy - Okamžik posledního přístupu - Použít pro procházení souborů aplikační rámec pro přístup k úložišti z Android (SAF) – k dispozici v KitKat a novějším - Aplikační rámec přístupu k úložišti + Vytvoření + Změna + Přístup + Použít \"Android storage access framework\" pro procházení souborů (k dispozici v Android KitKat a novějším) + Storage Access Framework Varování - Vaše heslo nejspíš obsahuje znaky nepodporované znakovou sadou Latin-1, která je používaná v .kdb souborech. Protože jsou převedeny na stejné písmeno, je doporučeno heslo změnit, aby bylo bezpečnější. + Nepoužívejte v hesle pro .kdb soubory znaky nepodporované znakovou sadou Latin-1 (nepoužívejte znaky s diakritikou). Opravdu chcete ponechat databázi nechráněnou (bez hesla)? Opravdu nechcete používat šifrovací klíč? - Otisk prstu je zařízením podporován, ale není nastavený - Očekávání otisků prstů + Otisk prstu je zařízením podporován, ale není nastavený. + Snímání otisku prstu Šifrované heslo uloženo Problém s neplatným otiskem prstu. Obnovte své heslo. Otisk prstu není rozpoznán Problém s otiskem prstu: %1$s Použít pro uložení tohoto hesla otisk prstu - Pro tuto databázi zatím není uloženo žádné heslo + Tato databáze zatím není chráněna heslem. Historie Vzhled Obecné Automatické vyplnění - KeePass DX služba automatického vyplňování + KeePass DX automatické vyplňování formulářů Přihlašovat se pomocí KeePass DX Nastavit výchozí službu automatického vyplňování - Zapnutím této služby se vám budou snáze vyplňovat formuláře z ostatních aplikacích - Délka hesla - Nastavit výchozí délku vytvářených hesel + Povolit rychlé automatické vyplňování formulářů v ostatních aplikacích + Délka generovaného hesla + Nastavení výchozí délky generovaných hesel Znaků hesla - Nastavit výchozí počet znaků pro vytváření hesel + Nastavit povolené znaky pro generátor hesel Schránka Oznamování schránky Zapnout oznamování schránky o kopírování kolonek položky - Pokud vaše zařízení není schopné automaticky mazat výstřižky ze schránky, smažte zkopírovanou položku z historie schránky ručně. + Vymazání historie schránky, pokud automatické vyčištění schránky selže. Zamknout Zamknout obrazovku Při zhasnutí obrazovky uzamknout databázi Jak nastavit rychlé odemykání otiskem prstu? - Nastavte svůj otisk prstu pro své zařízení v + Uložte svůj otisk prstu pro své zařízení v „Nastavení“ → „Zabezpečení“ → „Otisk prstu“ - Zadejte své heslo v Keepass DX - Naskenujte otisk prstu a ukládejte tak hlavní heslo zabezpečeně - Pro otevření databáze při odškrtnuté volbě heslo, naskenujte otisk svého prstu + Zadejte heslo pro zamčení databáze + Přiložte prst na snímač otisku prstu a zabezpečte tak heslo k databázi otiskem. + Pro otevření databáze s vypnutým heslem přiložte prst na snímač otisku prstu. Použítí Otisk - Skenování otisků prstů - Otevírat databáze otiskem prstu + Snímání otisku prstu + Otevírat databázi otiskem prstu Smazat šifrovací klíče Smazat všechny šifrovací klíče související s rozpoznáváním otisku prstu - Opravdu chcete smazat všechny klíče související s otisky prstů? + Opravdu chcete smazat všechny klíče přiřazené k otiskům prstů\? Toto funkci se nedaří spustit. Verze %1$s vámi používaného systému Android je starší, než minimální požadovaná %2$s. Hardware nebyl rozpoznán. Název souboru Popis umístění Přiřadit hlavní klíč - Vytvořit KeePass soubor + Vytvořit novou databázi Bajtů - Popis umístění souboru + Cesta k souboru Zobrazit úplný popis umístění souboru - Použít Koš - Namísto smazání, přesunout skupinu či položku nejprve do „Koše“ - Pro zápis do databáze potřebuje KeePass DX oprávnění na externí úložiště - Aby bylo možné načítat URI adresy, které nepocházejí z poskytování obsahu, potřebuje KeePass DX oprávnění k přístupu na externí úložiště - Oprávnění k externímu úložišti odepřeno - Akci nelze bez oprávnění k externímu úložišti provést - Písmo kolonky - Čitelnost znaků v kolonkách můžete přizpůsobit změnou písma - Automaticky otevřít označený soubor - Po označení ve správci souborů na obrazovce pro výběr soubor automaticky otevřít - Zkopírovat heslo - Umožnit kopírovat heslo a chráněné kolonky do schránky + Použít koš + Přesunout skupiny či položky do „Koše“ místo smazání + KeePass DX potřebuje oprávnění k externímu úložišti, aby mohl zapsat do databáze. + KeePass DX potřebuje oprávnění k externímu úložišti, aby mohl načítat URI adresy, které nepocházejí od poskytovatele obsahu. + Oprávnění k externímu úložišti odepřeno. + Akci nelze provést bez oprávnění k externímu úložišti. + Písmo položek + Čitelnost znaků v položkách můžete přizpůsobit změnou písma + Otevírat soubory vybráním + Otevírat soubory vybráním ve správci souborů + Důvěřovat schránce + Umožnit kopírovat heslo a chráněné položky do schránky VAROVÁNÍ: Schránka je sdílena všemi aplikacemi. Pokud jsou do ní zkopírovány citlivé údaje, může se k nim dostat další software. - VAROVÁNÍ: Vypnutí této funkce může vést k nemožnosti otevírat nebo ukládat databáze - Odkaz na KDBX soubor který otevřít + VAROVÁNÍ: Vypnutí této funkce může znemožnit otevírání nebo ukládání databází. + Odkaz na otevíranou databázi Název databáze Popis databáze Verze databáze - Vzhled textu - Vzhled aplikace + Text + Aplikace Ostatní Klávesnice Magikeyboard - Aktivovat vlastní klávesnici která snadno vyplní hesla a identitu + Aktivovat vlastní klávesnici, která snadno vyplní hesla a další položky Nastavení Magikeyboard - Jak nastavit klávesnici pro zabezpečené vyplňování formulářů? - Zapněte Magikeyboard v nastavení zařízení. - „Nastavení“ → „Jazyk a vstup“ → „Stávající klávesnice“ a zvolte nějakou. + Nastavit klávesnici pro bezpečné vyplňování formulářů. + Zapnout \"Magikeyboard\" v nastavení zařízení. + „Nastavení“ → „Jazyk a vstup“ → „Stávající klávesnice“ a zvolte některou. nebo („Nastavení“ → „Jazyk a vstup“ → „Virtuální klávesnice“ a zvolte nějakou.) Když potřebujete vyplnit formulář, zvolte Magikeyboard. - Na Magikeyboard můžete snadno přepnout ze své hlavní klávesnice a to stisknutím jazykového tlačítka na své klávesnici, dlouhým podržením mezerníku, nebo (pokud není k dispozici) pomocí: + Na Magikeyboard můžete přepnout ze své obvyklé klávesnice stisknutím jazykového tlačítka na své klávesnici, dlouhým podržením mezerníku, a pokud nejsou k dispozici tak pomocí: Vyberte položku klávesou. - Vyplnit kolonky pomocí prvků položky. + Vyplnit hodnoty pomocí prvků položky. Zamknout databázi. - Vrátit se zpět k hlavní klávesnici. + Vrátit se zpět k obvyklé klávesnici. Umožnit nevyplněné heslo - Zpřístupnit otevírací tlačítko pokud není vybrána žádná identifikace hesla + Povolit tlačítko \"Otevřít\" i když není vybráno žádné heslo Pouze pro čtení - Ve výchozím stavu otevírat databáze pouze pro čtení + Ve výchozím stavu otevírat databázi pouze pro čtení Výukové obrazovky Zvýraznit prvky a naučit se jak aplikace funguje - Resetovat výukové obrazovky - Resetovat zobrazení výukových položek - Reset výukových obrazovek + Nastavit výukové obrazovky do výchozího stavu + Opět zobrazit všechny výukové položky + Nastavit výukové obrazovky do výchozího stavu Vytvořte svůj databázový soubor - Zatím ještě KeePass DX neznáte, vytvořte svůj první soubor pro správu hesel. + Vytvořte svůj první soubor pro správu hesel. Otevřít existující databázi - Správce KeePass jste už používali. Stačí jen otevřít KDBX soubor z prohlížeče souborů. + Otevřete svou dříve používanou databázi ze správce souborů a pokračujte v jejím používání. Postačí odkaz na umístění souboru Databázi také můžete otevírat pomocí fyzického odkazu (se file:// a content:// například). Přidejte nové položky do databáze Přidat položky pro správu vašich digitálních identit. \n \nPřidat skupiny (ekvivalent složek) pro uspořádávání položek a databáze. - Snadno hledejte položky - Hledejte položky podle názvu, uživatelského jméno nebo dalších kolonek a snadno se tak dostávejte ke svým heslům. - Odemkněte databázi pomocí otisku prstu - Propojte své heslo a otisk prstu pro snadné odemykání databáze. + Hledejte v položkách + Zadejte název, uživatelské jméno nebo jiné položky k nalezení svých hesel. + Odemykání databáze otiskem prstu + Propojte své heslo a otisk prstu pro rychlé odemykání databáze. Upravit položku - Upravte svou položku pomocí vlastních kolonek, odkazy na data fondu je možné přidat mezi kolonky různých položek. - Vytvořit silné heslo - Vytvořit odolné heslo které přiřadit položce, snadno ji určit dle kritérií formuláře a nezapomenout bezpečné heslo které si můžete zapamatovat. + Přidejte ke své položce vlastní kolonky. Společná data mohou být sdílena mezi více různými kolonkami. + Vytvoření silného hesla. + Vygenerujte silné heslo pro svou položku, odpovídající důležitosti vyplňovaného formuláře a nezapomeňte jej bezpečně uložit a používat. Přidat vlastní kolonky - Pokud chcete zaregistrovat základní kolonku, která není ve výchozím stavu k dispozici, jednoduše vyplňte novou. Také ji můžete vizuálně chránit. + Pokud chcete zaregistrovat základní kolonku, která není ve výchozím stavu k dispozici, jednoduše vyplňte novou kolonku. Novou kolonku můžete také nastavit jako chráněnou. Odemknout databázi - Povolit pouze čtení + Povolit pouze čtení z databáze Změnit režim otevírání pro dané sezení. \n \nV režimu pouze pro čtení zabráníte nechtěným změnám do databáze. @@ -352,13 +352,13 @@ \n \nJe možné použít vícero metod vyplňování formulářů. Použijte svou oblíbenou. Uzamčení databáze - Rychle uzamkněte databázi. Je možné nastavit, aby se aplikace zamkla – po chvíli a po zhasnutí obrazovky. - Řaďte položky - Řaďte položky a skupiny podle konkrétních parametrů. + Rychlé uzamčení databáze. Je možné nastavit, aby se databáze zamkla po určitém čase a také po zhasnutí obrazovky. + Řazení položek + Vyberte řazení položek a skupin. Zapojit se - Zapojte se a pomozte zvýšit stabilitu, zabezpečení a přidávat více funkcí. + Zapojte se a pomozte zvýšit stabilitu, bezpečnost a přidávání dalších funkcí. - Narozdíl od mnoha aplikací pro správu hesel, tato je bez reklam, svobodný software a neodesílá nikam žádné osobní údaje a to ani ve své variantě zdarma. + Narozdíl od mnoha aplikací pro správu hesel, tato je bez reklam, je svobodným softwarem a neodesílá nikam žádné osobní údaje, bez ohledu na to, jakou verzi používáte. Zakoupením varianty „pro“ získáte přístup k této vizuální funkci a hlavně pomůžete uskutečnění komunitních projektů. Tato vizuální funkce je k dispozici díky vaší štědrosti. Pro zajištění svobody nás všech a pokračování aktivity, počítáme s vaším přispěním. @@ -369,7 +369,7 @@ povzbudíte vývojáře k přidávání nových funkcí a opravování chyb dle vašich připomínek. Mnohé díky za vaše přispění. Tvrdě pracujeme na brzkém vydání této funkce. - Nezapomeňte aplikaci aktualizovat. + Nezapomeňte aplikaci aktualizovat instalováním nových verzí. Stáhnout Zapojit se @@ -379,9 +379,34 @@ AES KDF Argon2 - Vyberte motiv vzhledu - Změnit motiv vzhledu aplikace změnou barev - Vyberte sadu ikon - Změnit sadu ikon aplikace + Vzhled aplikace + Motiv vzhledu aplikace + Sada ikon + Sada ikon používaných v aplikaci - +Sestavení %1$s + Magikeyboard + Magikeyboard (KeePass DX) + Magikeyboard nastavení + + Položka + + Uchování položky + Doba uchování položky v Magikeyboardu + + Zobrazit oznámení + Zobrazit oznámení, když je položka v Magikeyboardu dostupná + Položka + %1$s dostupné v Magikeyboardu + %1$s + + Vymazat při zavření + Vymazat položku z Magikeyboardu při zavření oznámení + Vzhled + + Vzhled klávesnice + + Klávesy + Vibrovat při stisku klávesy + Vydat zvuk při stisku klávesy + From af32ef24db21b991d7d21f7607ac7e8a9fa7dced Mon Sep 17 00:00:00 2001 From: MESUT AKCAN Date: Sun, 18 Nov 2018 07:40:06 +0000 Subject: [PATCH 019/289] Translated using Weblate (Turkish) Currently translated at 20.3% (70 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 0ce3004f3..e8beb7a21 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -46,4 +46,27 @@ Veritabanı okunamadı. Yolun doğru olduğundan emin olun. Bir isim girin. + Şimdi panoyu temizlemek için kaydırın + %1$s dosyasını panoya kopyalamak için seçin + Veritabanı anahtarı alınıyor… + Veritabanı içeriği deşifre ediliyor… + Varsayılan veritabanı olarak kullan + Rakamlar + KeePass DX © %1$d Kunzisoft, KESİNLİKLE HİÇBİR GARANTİ VERMEZ; Bu bir libre yazılımıdır ve bunu GPL sürüm 3 veya üstü koşullarında yeniden dağıtabilirsiniz. + Süre sonu + Anahtar dosya + ARCFOUR akış şifresi desteklenmiyor. + Bu URI, KeePass DX\'te işlenemedi. + Bir anahtar dosyası seçin. + Tüm veritabanınızı yüklemek için bellek yok. + Veritabanınız yüklenemedi. + Anahtar yüklenemedi. KDF \"Bellek Kullanımı\" nı azaltmaya çalışın. + En az bir parola oluşturma türü seçilmelidir. + Parolalar uyuşmuyor. + \"Dönüşüm turları\"nı bir sayı yapın. + \"Dönüşüm turları\" çok yüksek. 2147483648\'e ayarlayın. + Her dizenin bir alan adı olmalıdır. + Bir başlık ekle. + \"Uzunluk\" alanına pozitif bir tam sayı girin. + Otomatik doldurma hizmeti etkinleştirilemedi. From 71a77250d0692f135d9b1797f667ff4ca0ac79ef Mon Sep 17 00:00:00 2001 From: liushuyu011 Date: Mon, 19 Nov 2018 05:00:44 +0000 Subject: [PATCH 020/289] Translated using Weblate (Chinese (Simplified)) Currently translated at 58.6% (202 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 74 ++++++++++++---------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e1120ec26..bc26cef5c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -24,17 +24,17 @@ 接受 添加条目 添加文件夹 - 算法 + 加密算法 离开应用锁定延时 - 本应用处于非前台状态之后经过多久自动锁定数据库 + 本应用处于非前台状态后经过多久自动锁定数据库 应用 应用设置 括号 - 浏览文件需要安装 Open-Intents File Manager 软件,点击下面即可安装。由于一些文件管理软件之间的差异,在第一次打开时可能无法正常浏览。 + 安装 OpenIntents File Manager 应用来选取文件 取消 已清空剪贴板 剪贴板清空延时 - 复制用户名或密码到剪贴板后清除的时间 + 剪贴板清空时间 复制 %1$s 到剪贴板 创建数据库密钥… 数据库 @@ -42,7 +42,7 @@ 设为默认数据库 数字 KeePass DX \u00A9 %1$d Kunzisoft 软件不带有绝对担保;本应用是自由软件,您可在遵循 GPL 3 或者此开源协议的更高版本的情况下重新发布。 - 选择一个已有数据库 + 打开已有数据库 访问时间 取消 备注 @@ -63,17 +63,17 @@ 无法打开链接。 必须输入文件名。 无法创建文件: - 数据库无效或主密钥无法识别。 - 路径无效。 + 无法读取数据库。 + 请确保路径是正确的。 必须输入用户名。 - 必须输入密码或添加密钥文件。 - 此设备因运行内存不足而无法解析数据库。 - 必须选择一个或更多密码生成类型 + 请选择密钥文件。 + 运行内存不足,无法解析数据库。 + 必须至少选择一种密码生成类型。 密码不匹配。 - “次数”必须是数字。 - “次数”过多。最大值为 2147483648。 - 必须输入标题。 - 请在“长度”字段输入一个正整数 + “变换次数”必须是数字。 + “变换次数”过多。已设置为 2147483648。 + 请添加标题。 + 请在“长度”字段输入一个正整数。 找不到文件。 文件浏览器 生成密码 @@ -93,10 +93,10 @@ 列表文字大小 正在加载数据库… 小写 - 密码掩膜 - 默认隐藏密码 + 隐藏密码 + 默认使用星号 (***) 隐藏密码 关于 - 改变主密钥 + 更改主密钥 设置 数据库设置 删除 @@ -110,8 +110,8 @@ 连字符 从不 没有搜索结果 - 没有能处理此链接的程序。 - 最近数据库: + 需要安装网络浏览器才能打开这个 URL。 + 最近使用过的数据库 正在创建新数据库… 正在处理… 记住密钥文件的位置 @@ -120,8 +120,8 @@ Rijndael (AES) Root 加密次数 - 更多的加密次数能对暴力破解攻击能提供额外保护,但也会增加加载和保存的时间。 - 次数 + 更多的加密次数能更好地抵抗暴力破解攻击,但也会增加读取和保存的时间。 + 变换次数 正在保存数据库… 空格 搜索 @@ -132,7 +132,7 @@ 下划线 不支持的数据库版本。 大写 - SD卡目前为只读状态。您可能无法将更改保存到数据库。 + 请授予 SD 卡写入权限来保存数据库。 您的 SD 卡目前尚未挂载至您的设备上。无法加载或创建您的数据库。 输入密码和/或一个密钥文件来解锁你的数据库。 @@ -161,8 +161,8 @@ ASCⅡ 拓展区字符 允许 剪切板错误 - 一些三星设备的剪切板实现方式有 bug,会导致无法从应用中复制。更多细节: - 剪切板清空失败 + 一些三星设备不让应用程序使用剪切板。 + 无法清空剪切板 滑动以清空剪切板 ChaCha20 @@ -174,13 +174,13 @@ 切换本应用中使用的图标风格 编辑条目 - 密钥推导函数/功能 (Key Derivation Function) + 密钥推导函数 找不到条目。 - 无法加载数据库 - 无法加载密钥,尝试降低 KDF 使用的内存。 + 无法加载数据库。 + 无法加载密钥,尝试降低 KDF 的“内存使用”值。 无法启用自动填充服务。 无法将文件夹移至它自身之下。 - 找不到文件。尝试使用内容提供器重新打开它。 + 找不到文件。尝试使用文件浏览器重新打开它。 算法无效。 没有密钥文件。 密钥文件为空文件。 @@ -192,20 +192,20 @@ 粘贴 取消 显示密码 - 移除指纹密钥 + 删除保存的指纹 只读 - 读写 + 可修改 搜索时忽略备份条目 从搜索结果中隐藏 “备份” 文件夹的搜索结果(仅适用于 .kdb 文件) 保护 只读 - KeePass DX 没有写入数据库所在位置的权限,所以此数据库将以只读的形式打开。 + KeePass DX 需要写入权限才能修改数据库中的内容。 从 Android 4.4.4 KitKat 开始,一些设备不再允许应用直接写入 SD 卡。 最近文件历史 记住最近使用的文件名 加密整个数据库时使用的算法。(密码, \n、用户名、笔记和所有数据库中的数据将使用此算法来加密) - 为了生成加密算法所需的密钥,通过随机加盐推导算法处理了经过压缩的主密钥(SHA-256)。 + 将会使用随机加盐推导算法变换主密钥以生成加密算法所需的密钥。 内存使用量 密钥推导算法使用的内存(以二进制字节计)。 并行 @@ -232,7 +232,7 @@ 指纹 文件名 路径 - 创建 KeePass 文件 + 创建新数据库 B 显示文件路径 显示完整的文件路径 @@ -253,4 +253,12 @@ 贡献 选择各种颜色的应用主题 + 每个字符串必须有一个字段名。 + 字段名 + 字段值 + %1 的副本 + 最低优先 ↓ + 使用 Android 存储访问框架 (SAF)浏览文件(需要 KitKat 或更高) + 存储访问框架 + 请避免在 .kbd 文件中保存含有 Latin-1 以外的其他字符的密码,因为这些字符将会被转换为同一字符。 From 06aec23c8ae84098c1c3d6c4b9291025ecd35d3c Mon Sep 17 00:00:00 2001 From: MESUT AKCAN Date: Wed, 21 Nov 2018 15:58:03 +0000 Subject: [PATCH 021/289] Translated using Weblate (Turkish) Currently translated at 27.2% (94 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e8beb7a21..d07793024 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -69,4 +69,28 @@ Bir başlık ekle. \"Uzunluk\" alanına pozitif bir tam sayı girin. Otomatik doldurma hizmeti etkinleştirilemedi. + Bir grubu kendine taşıyamazsın. + Alan adı + Alan değeri + Dosya bulunamadı. + Dosya bulunamadı. Dosya tarayıcınızda yeniden açmayı deneyin. + Dosya tarayıcı + Parola üret + parolayı onayla + oluşturulan parola + Grup adı + anahtar dosya + uzunluk + parola + Parola + F-Droid\'den yükleyin + "Play Store\'dan yükleyin " + Parola veya anahtar dosya okunamadı. + Yanlış algoritma. + Veritabanı biçimi tanımlanamadı. + Hiç anahtar dosya yok. + Anahtar dosya boş. + Uzunluk + Kullanıcı adlarını göster + Giriş listelerinde kullanıcı adlarını göster From 03456069032921da6cca01607e489df81f411c16 Mon Sep 17 00:00:00 2001 From: mohammadA Date: Fri, 23 Nov 2018 03:31:43 +0000 Subject: [PATCH 022/289] Translated using Weblate (Arabic) Currently translated at 35.1% (121 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/ --- app/src/main/res/values-ar/strings.xml | 94 ++++++++++++++------------ 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index f95c06d05..ac847762f 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -30,48 +30,48 @@ تمديد ASCII إلغاء السماح - تم مسح الحافظة + مُسِحت الحافظة خطأ في الحافظة - فشلت عملية مسح الحافظة - قم بالسحب لمسح الحافظة فورًا + تعذَّر مسح الحافظة + اسحب لمسح الحافظة فورًا قاعدة البيانات - فك تعمية محتويات قاعدة البيانات … + يفك تعمية محتوى قاعدة البيانات… أرقام إلغاء - التعليقات - تأكيد الكلمة السرية - تم إنشاؤه + ملاحظات + تأكيد كلمة السر + أُنشئ معدل - لم يتم العثور على بيانات الإدخال. + تعذر العثور على بيانات المُدخلة. كلمة السر حفظ العنوان - عنوان الرابط - إسم المستخدم + رابط + اسم المستخدم هذا الملف موجود مِن قَبل. تعذر فتح الرابط. - إسم الملف مطلوب. + ادخل اسمًا للملف. تعذر إنشاء الملف : - مسار غير صالح. - الإسم لازم. - الكلمات السرية غير متطابقة. - العنوان مطلوب. + تأكد أن المسار صحيح. + ادخل اسمًا. + كلمتا السر غير متطابقتين. + اكتب عنوانًا. اسم الحقل قيمة الحقل - الملف غير موجود. - توليد كلمةٍ سرية - تأكيد الكلمة السرية - إسم المجموعة + تعذر إيجاد الملف. + توليد كلمة سر + تأكيد كلمة السر + اسم المجموعة الطول كلمة السر كلمة السر التثبيت مِن متجر غوغل للتطبيقات التثبيت مِن مستودَع أف-درويد للتطبيقات - خوارزمية غير صالحة. + خوارزمية خاطئة. الطول عدد عناصر القائمة حجم النص في قائمة العناصر - جار تحميل قاعدة البيانات … + يحمل قاعدة البيانات… حروف صغيرة إخفاء كلمات المرور بشكل افتراضي عن التطبيق @@ -123,36 +123,44 @@ تأمين قاعدة البيانات الملاحظات : -KeePass DX تنفيذ الروبوت مدير KeePass كلمة المرور. - إضافة كلمة للقاموس - دخول فقط +تنفيذ أندرويد لمدير كلمات السر «كي‌باس» + إضافة مدخلة + تحرير مدخلة وظيفة اشتقاق المفتاح مهلة التطبيق - الوقت قبل قفل قاعدة البيانات عندما يكون التطبيق غير نشط - تصفح الملف يتطلب إدارة الملف أوبينينتينتس لتثبيت، انقر فوق أدناه للقيام بذلك. بسبب بعض المراوغات في إدارة الملفات، التصفح قد لا تعمل بشكل صحيح في المرة الأولى. - بعض الهواتف الروبوت سامسونج يكون خلل في تنفيذ حافظة تسبب نسخ من تطبيقات إلى فشل. مزيد من التفاصيل: + مدة الانتظار قبل إقفال التطبيق + تصفَّح الملفات بتثبيت مدير الملفات OpenIntents + بعض هواتف سامسونغ لا تسمح للتطبيقات باستعمال الحافظة. مهلة الحافظة - الوقت قبل مسح الحافظة بعد نسخ اسم المستخدم أو كلمة المرور - نسخ صورة رمز QR الى الحافظة - إنشاء مفتاح قاعدة البيانات… + مدة التخزين في الحافظة + اختر لنسخ %1$s إلى الحافظة + يجلب مفتاح قاعدة البيانات… استخدام هذا كقاعدة البيانات الافتراضية - KeePass DX \u00A9 %1$d د $ %1 كونزيسوفت تأتي مع \"الضمان لا على الإطلاق\"؛ هذا هو البرمجيات الحرة، وكنت أهلا إعادة توزيعه تحت شروط إصدار الترخيص 3 أو في وقت لاحق. - الوصول إلى - انتهاء صلاحية - مفتاح الملف - تشفير دفق أركفوور غير معتمد. - KeePass DX تنفيذ الروبوت مدير KeePass كلمة المرور. + KeePass DX © %1$d Kunzisoft لا يأتي معه أي ضمان، وهو برمجية حرة نرحب بتوزيعها بشروط رخصة غنو العمومية النسخة الثالثة أو ما بعدها. + نُفذ إليه + تنتهي صلاحيته في + ملف المفتاح + تشفير دفق ARCFOUR غير مدعوم. + تعذرت معاملة هذا الرابط في KeePass DX. تعذر إنشاء الدليل الأصل. - كلمة محجوزة غير معروفة. - فشل في إنشاء ملف مفاتيح الرموز. - الجهاز نفدت الذاكرة أثناء تحليل قاعدة البيانات الخاصة بك. - لا يمكن أن تكون قاعدة البيانات تحميل + تعذرت قراءة قاعدة البيانات. + اختر ملف مفتاح. + لا ذاكرة لتحميل قاعدة البيانات كاملة. + تعذر تحميل قاعدة البيانات. غير قادر على تحميل المفتاح، في محاولة لتقليل الذاكرة المستخدمة من قبل KDF. يجب تحديد كلمة مرور واحد على الأقل نوع الجيل يجب أن تكون \"جولات\" عددا. \"جولات\" كبيرة جداً. الإعداد إلى 2147483648. يجب أن يكون لكل سلسلة اسم حقل. - قم بإدخال عدد صحيح موجب في الحقل \"المدة\" - لا يمكن تمكين خدمة الملء التلقائي. - غير قادر على نقل مجموعة إلى نفسه. + أدخل عددًا صحيحًا موجبًا في حقل «الطول». + تعذر تمكين خدمة الملء التلقائي. + غير قادر على نقل مجموعة إلى نفسها. + تعذر إيجاد الملف. جرِّب فتحه من متصفح ملفات. + متصفح الملفات + تعذرت قراءة كلمة السر أو ملف المفتاح. + تعذر تمييز نسق قاعدة البيانات. + لا يوجد ملف مفتاح. + ملف المفتاح فارغ. + أظهر أسماء المستخدمين + أظهر أسماء المستخدمين في قوائم المدخلات From 792b3b7a8157b70a857f579b3921b21476f4c502 Mon Sep 17 00:00:00 2001 From: MESUT AKCAN Date: Fri, 23 Nov 2018 09:01:38 +0000 Subject: [PATCH 023/289] Translated using Weblate (Turkish) Currently translated at 33.0% (114 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index d07793024..1011a8563 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -93,4 +93,24 @@ Uzunluk Kullanıcı adlarını göster Giriş listelerinde kullanıcı adlarını göster + Liste öğelerinin boyutu + Öğe listesindeki metin boyutu + Veritabanı yükleniyor… + Küçük harf + "Parolaları gizle " + Parola maskesi. Varsayılan (***) + Hakkında + Ana anahtarı değitir + 1$s kopyası + Ayarlar + Uygulama ayarları + Form doldurma + Veritabanı ayarları + Bağış Yap + Düzen + Kopyala + Taşı + Yapıştır + Sil + İptal From d54abf66fe637a709a3c229357418b515bdc3820 Mon Sep 17 00:00:00 2001 From: Mesut Akcan Date: Thu, 29 Nov 2018 09:12:01 +0000 Subject: [PATCH 024/289] Translated using Weblate (Turkish) Currently translated at 48.7% (168 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 55 ++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 1011a8563..c8b931d89 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -113,4 +113,59 @@ Yapıştır Sil İptal + "Parolayı gizle " + Veritabanını kilitle + + Ara + Parolayı göster + Kaydedilen parmak izini silin + URL\'a git + Yazma korumalı + Değiştirilebilir + Asla + Arama sonucu bulunamadı + Bu URL\'u açmak için bir web tarayıcısı yükleyin. + Mevcut veritabanını aç + Yeni veritabanı oluştur + Son veritabanları + Yedek girişleri arama + Arama sonuçlarından \"Yedekleme\" grubunu çıkar (yalnızca .kdb dosyaları için geçerlidir) + Yeni veritabanı oluştur… + Çalışıyor… + Koruma + Yazma korumalı + Veritabanınızdaki herhangi bir şeyi değiştirmek için KeePass DX\'in yazma iznine ihtiyacı var. + Android KitKat ile başlayan bazı cihazlar artık uygulamaların SD karta yazmasına izin vermiyor. + Son dosya geçmişi + Son dosya adlarını hatırla + Veritaban anahtar dosyaların yerini hatırlar + Anahtar dosya kaydet + Kaldır + Kök + Tüm veriler için veritabanı şifreleme algoritması kullanılmıştır. + Şifreleme algoritmasının anahtarını üretmek için ana anahtar, rastgele anahtar türetme işlevi kullanılarak dönüştürülür. + Dönüşüm turları + Ek şifreleme turları, kaba kuvvet saldırılarına karşı daha yüksek koruma sağlar, ancak yükleme ve kaydetmeyi gerçekten yavaşlatabilir. + dönüşüm turları + Hafıza kullanımı + Anahtar türetme işlevi tarafından kullanılacak bellek miktarı (ikili bayt cinsinden). + Paralellik + Anahtar türev fonksiyonu tarafından kullanılan paralellik derecesi (yani iplik sayısı). + Veritabanı kaydediliyor… + Boşluk + Ara + Sırala + En düşük önce +\n + Grup önce + Alttaki geri dönüşüm kutusu + Doğal Veritabanı + Başlık + Kullanıcı adı + Oluşturma + Değişiklik + Erişim + Özel + Ara + Arama Sonuçları From d4a31567cd55844119b0619f42c855719ba2f79c Mon Sep 17 00:00:00 2001 From: Bruno Guerreiro Date: Fri, 30 Nov 2018 12:26:29 +0000 Subject: [PATCH 025/289] Translated using Weblate (Portuguese (Portugal)) Currently translated at 36.8% (127 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_PT/ --- app/src/main/res/values-pt-rPT/strings.xml | 94 ++++++++++++++++------ 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 8adeea46f..396a291c9 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -1,4 +1,4 @@ - + - + Comentários: Página Web: - KeePass DX é uma implementação para o Android do gerenciador de palavras-passe KeePass. + Uma implementação do gestor de palavras-chave KeePass para Android Aceitar Adicionar entrada Adicionar grupo @@ -43,9 +41,9 @@ Tempo de espera da área de transferência Tempo antes da limpeza da área de transferência depois de copiado o nome de utilizador ou a palavra-passe. Selecione para copiar %1$s para a área de transferência - A criar a chave da base de dados… + A criar a chave da base de dados… Base de dados - A desencriptar conteúdo da base de dados… + A desencriptar conteúdo da base de dados… Utilizar esta base de dados como predefinida Dígitos KeePass DX \u00A9 %1$d Kunzisoft vem com ABSOLUTAMENTE NENHUMA GARANTIA; Este software é livre, e pode redistribui-lo conforme as condições da licença GPL versão 3 ou superior. @@ -53,13 +51,13 @@ Acedido Cancelar Comentários - Confirmar palavra-passe + Confirmar palavra-chave Criado Expira Ficheiro chave Modificado Dados da entrada não encontrados. - Palavra-passe + Palavra-chave Guardar Nome URL @@ -76,7 +74,7 @@ É obrigatório introduzir um nome. É obrigatório introduzir uma palavra-passe ou um ficheiro chave. O dispositivo ficou sem memória ao analisar a base de dados. Poderá ser muito grande para o seu dispositivo. - Pelo menos um tipo de geração de palavra-passe deve ser selecionado + Pelo menos um tipo de geração de palavra-chave deve ser selecionado. As palavra-passe não coincidem. As rondas devem ser um número. Rondas muito grandes. A definir para 2147483648. @@ -88,17 +86,17 @@ Ficheiro não encontrado. Ficheiro não encontrado. Tente reabrir a partir do seu fornecedor de conteúdo. Explorador de ficheiros - Gerar palavra-passe - confirmar palavra-passe - palavra-passe gerada + Gerar palavra-chave + confirmar palavra-chave + palavra-chave gerada Nome do grupo ficheiro chave comprimento - palavra-passe - Palavra-passe + palavra-chave + Palavra-chave Instalar pela Play Store Instalar pela F-Droid - Palavra-passe ou ficheiro chave inválidos. + Palavra-chave ou ficheiro chave inválidos. Algoritmo inválido. Formato da base de dados não reconhecido. O ficheiro chave não existe. @@ -106,7 +104,7 @@ Comprimento Tamanho da lista de grupos Tamanho do texto na lista de grupos - A carregar base de dados… + A carregar base de dados… Minúsculas Mascarar palavra-passe Ocultar as palavras-passe por predefinição @@ -117,11 +115,11 @@ Eliminar Fazer donativo Editar - Ocultar palavra-passe + Ocultar palavra-chave Bloquear base de dados Abrir Pesquisar - Mostrar palavra-passe + Mostrar palavra-chave Ir para o URL Menos Nunca @@ -130,8 +128,8 @@ Abrir base de dados recente : Não pesquisar entradas nas cópias de segurança Omitir o grupo \"Backup\" dos resultados da pesquisa (aplica-se apenas a ficheiros .kdb) - A criar nova base de dados… - Em funcionamento… + A criar nova base de dados… + Em funcionamento… Proteção Apenas leitura O KeePass DX não tem permissão para escrever na localização da base de dados, a sua base de dados será aberta apenas para leitura. @@ -146,7 +144,7 @@ Rondas de encriptação Rondas de encriptação altas providenciam proteção adicional contra ataques de força bruta, mas podem atrasar o tempo de carregamento e armazenamento. rondas - A guardar base de dados… + A guardar base de dados… Espaço Pesquisar Ordenação da BD @@ -160,7 +158,7 @@ Utilizar o Storage Access Framework (SAF) do Android para explorar os ficheiros (KitKat e superior) Storage Access Framework (SAF) Aviso - O formato .kdb apenas suporta o conjunto de caracteres Latin1. A sua palavra-passe pode conter caracteres fora deste conjunto. Todos os caracteres não Latin1 são convertidos para o mesmo carácter, o que reduz a segurança da sua palavra-passe. É recomendável alterar a sua palavra-passe. + O formato .kdb apenas suporta o conjunto de caracteres Latin1. A sua palavra-chave pode conter caracteres fora deste conjunto. Todos os caracteres não Latin1 são convertidos para o mesmo carácter, o que reduz a segurança da sua palavra-chave. É recomendável alterar a sua palavra-chave. O cartão SD não se encontra montado no seu dispositivo. Não será possível abrir ou criar a sua base de dados. Versão %1$s @@ -182,4 +180,52 @@ Média Grande - +ASCII Estendido + Permitir + Não foi possível abrir a sua base de dados. + Não foi possível carregar a chave. Tente baixar \"Uso de Memória\" do KDF. + Não pode mover um grupo para si mesmo. + Mostrar nomes de utilizador + Cópia de %1$s + Preenchimento de formulário + Copiar + Mover + Colar + Cancelar + Apagar de impressão digital gravada + Protegido contra escrita + Pode ser modificado + Criar nova base de dados + Para gerar uma chave para o algoritmo de encriptação, a chave mestre comprimida (SHA-256) é transformada usando uma função de derivação de chave (com um salt aleatório). + Uso de memória + Quantidade de memória (em bytes binários) a ser usada pela função de derivação de chave. + Paralelismo + Grau de paralelismo (ou seja, número de threads) usado pela função de derivação de chave. + Ordenar + Ascendente + Grupos primeiro + Reciclagem no fundo + Título + Nome de utilizador + Data de criação + Última modificação + Último acesso + Quer realmente usar uma palavra-chave vazia\? + Tem a certeza que não quer usar uma chave de encriptação\? + Construir %1$s + Impressão digital suportada, mas não configurada para dispositivo. + Palavra-chave encriptada armazenada + Impressão digital não reconhecida + Problema da Impressão digital: %1$s + Use a impressão digital para armazenar esta palavra-chave + Ainda não há nenhuma palavra-chave armazenada nesta base de dados. + Histórico + Aparência + Geral + Preenchimento automático + Serviço de Preenchimento Automático do KeePass DX + Entrar com KeePass DX + Definir como serviço de preenchimento automático padrão + Ativar o serviço para preencher formulários em outras aplicações + Tamanho da palavra-chave gerada + From 310ae583888f359f59d915a6abc5b6bbae8e225e Mon Sep 17 00:00:00 2001 From: Mesut Akcan Date: Sat, 1 Dec 2018 10:03:07 +0000 Subject: [PATCH 026/289] Translated using Weblate (Turkish) Currently translated at 51.9% (179 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c8b931d89..a1657f30e 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -168,4 +168,15 @@ Özel Ara Arama Sonuçları + Eksi + Altı çizili + Desteklenmeyen veritabanı sürümü. + Büyük harf + Dosya taraması için Android depolama erişim yapısını (SAF) kullan (KitKat sonrası) + Depolama erişim yapısı + Uyarı + Tümü aynı harfe dönüştürüldüğünden .kbd dosyalarındaki Latin-1 dışındaki şifre karakterlerinden kaçının. + Veri tabanı değişikliklerini kaydetmek için SD karta yazma erişimi verin. + Bir veritabanı oluşturmak veya yüklemek için SD kartı takın. + Gerçekten parolasız açma koruması mı istiyorsunuz\? From fd66e9acb8331a43ba4f36cf88c0678c0ef19897 Mon Sep 17 00:00:00 2001 From: Mesut Akcan Date: Mon, 3 Dec 2018 09:01:28 +0000 Subject: [PATCH 027/289] Translated using Weblate (Turkish) Currently translated at 56.8% (196 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index a1657f30e..1b50db3ef 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -179,4 +179,21 @@ Veri tabanı değişikliklerini kaydetmek için SD karta yazma erişimi verin. Bir veritabanı oluşturmak veya yüklemek için SD kartı takın. Gerçekten parolasız açma koruması mı istiyorsunuz\? + Herhangi bir şifreleme anahtarı kullanmak istemediğinize emin misiniz\? + Sürüm %1$s + Yapı %1$s + Parmak izi taraması desteklenir, ancak kurulmaz. + Parmak izi tarama + Şifreli parola saklandı + Parmak izi anahtarı okunamadı. Şifreni geri yükle. + Parmak izi tanınamadı + Parmak izi sorunu: %1$s + Bu şifreyi saklamak için parmak izini kullanın + Bu veritabanının henüz bir parolası yok. + Geçmiş + Görünüm + Genel + Otomatik Doldurma + KeePass DX formu otomatik doldurma + KeePass DX ile giriş yap From 35751844b01e91b902ca9189a7f306aa5b7c3892 Mon Sep 17 00:00:00 2001 From: ssantos Date: Tue, 4 Dec 2018 19:24:09 +0000 Subject: [PATCH 028/289] Translated using Weblate (German) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d8efaa4c1..f2ecb9979 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -307,11 +307,11 @@ Datenbank mit Fingerabdruck entsperren Verknüpfen Sie Ihr Passwort und Ihren Fingerabdruck um Ihre Datenbank schnell zu entsperren. Eintrag bearbeiten - Bearbeiten Sie Ihren Eintrag mit eigenen Feldern, Sie können zwischen Feldern aus verschiedenen Einträgen Verweise hinzufügen, um Daten zusammenzufassen. + Bearbeiten Sie Ihren Eintrag mit benutzerdefinierten Feldern. Pooldaten können zwischen verschiedenen Eingabefeldern referenziert werden. Ein starkes Passwort für ihren Eintrag erstellen. - Generieren Sie ein starkes Passwort zu Ihrem Eintrag, stellen Sie Ihre Kriterien zur Erstellung des Passworts nach den gewünschten Merkmalen ein und vergessen Sie nie wieder ein schweres, aber sicheres Passwort. + Generieren Sie ein sicheres Passwort, um es mit Ihrem Eintrag zu verknüpfen, definieren Sie es einfach nach den Kriterien des Formulars und vergessen Sie das sichere Passwort nicht. Benutzerdefinierte Felder hinzufügen - Registrieren Sie ein zusätzliches Feld und tragen Sie einen Wert dafür ein, den Sie wahlweise schützen können. + Registrieren Sie ein einfaches, nicht mitgeliefertes Feld, indem Sie ein neues Feld ausfüllen, das Sie auch schützen können. Ihre Datenbank entsperren Ein Feld kopieren Kopieren Sie einfach ein Feld, um es, wo Sie wollen, wieder einzufügen @@ -377,10 +377,10 @@ Schreibschutz aktivieren Datenbank standardmäßig schreibgeschützt öffnen - Ändern Sie den Modus bei Eröffnung der Sitzung. -\n -\n\"Schreibgeschützt\" verhindert unbeabsichtigte Änderungen an der Datenbank. -\n\"Veränderbar\" ermöglicht das Hinzufügen, Löschen oder Bearbeiten von Elementen. + Ändern Sie den Öffnungsmodus für die Sitzung. +\n +\n\"Write-protected\" verhindert unbeabsichtigte Änderungen an der Datenbank. +\nMit \"Modifizierbar\" können Sie alle Elemente hinzufügen, löschen oder ändern. Eintrag bearbeiten Datenbank kann nicht geladen werden. Laden des Schlüssels fehlgeschlagen; versuchen Sie, die \"Speicherplatznutzung\" von KDF zu verringen. From c5895ed61b98e7c6fe638f4f00c07f0de0296233 Mon Sep 17 00:00:00 2001 From: Mesut Akcan Date: Wed, 5 Dec 2018 14:10:25 +0000 Subject: [PATCH 029/289] Translated using Weblate (Turkish) Currently translated at 69.0% (238 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 42 ++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 1b50db3ef..3eaccda30 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -196,4 +196,46 @@ Otomatik Doldurma KeePass DX formu otomatik doldurma KeePass DX ile giriş yap + Varsayılan otomatik doldurma hizmetini ayarla + Diğer uygulamalardaki formları hızlı doldurmak için otomatik doldurmayı etkinleştirin + Oluşturulan parola boyutu + Oluşturulan parolaların varsayılan boyutunu ayarlar + Parola karakterleri + İzin verilen parola üreticisi karakterlerini ayarla + Pano + Pano bildirimleri + Giriş alanlarını kopyalamak için pano bildirimlerini etkinleştirin + Panodaki otomatik silme başarısız olursa, geçmişini elle silin. + Kilit + Ekran kilidi + Ekran kapalıyken veritabanını kilitle + Hızlı kilit açmak için parmak izi taraması nasıl ayarlanır\? + Cihazınız için taranmış parmak izinizi kaydet + \"Ayarlar\" → \"Güvenlik\" → \"Parmak İzi\" + Veritabanı kilitleme parolasını girin + Veritabanı kilit parolanızı güvenli bir şekilde saklamak için parmak izinizi tarayın. + Parola kapatıldığında veritabanını açmak için parmak izinizi tarayın. + Kullanım + Parmakizi + Parmak izi tarama + Veritabanını açmak için parmak izinizi taramanızı sağlar + Şifreleme anahtarlarını silin + Parmak izi tanıma ile ilgili tüm şifreleme anahtarlarını silin + Parmak izi tanıma ile ilgili tüm tuşları silmek istediğinizden emin misiniz\? + Bu özellik başlatılamadı. + Android sürümünüz %1$s, gerekli minimum %2$s sürümünü karşılamıyor. + İlgili donanım bulunamadı. + Dosya adı + Yol + Ana anahtar atayın + Bayt + Dosya yolu + Tam dosya yolunu görüntüle + Geri Dönüşüm Kutusunu kullan + Silmeden önce grupları ve girdileri \"Geri Dönüşüm Kutusu\"na taşır + KeePass DX\'in bir veritabanına yazmak için harici depolama iznine ihtiyacı var. + KeePass DX, bir içerik sağlayıcı tarafından sağlanmayan bir URI\'yi okumak için harici depolama iznine ihtiyaç duyar. + Harici depolama izni alınamadı. + Eylem, harici depolama izni olmadan gerçekleştirilemez. + Yazı tipi alanı From 8ecd0e0eb190ba35b2180391172cbb2684511b0d Mon Sep 17 00:00:00 2001 From: Mesut Akcan Date: Fri, 7 Dec 2018 14:04:37 +0000 Subject: [PATCH 030/289] Translated using Weblate (Turkish) Currently translated at 69.3% (239 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3eaccda30..8e6310a20 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -238,4 +238,5 @@ Harici depolama izni alınamadı. Eylem, harici depolama izni olmadan gerçekleştirilemez. Yazı tipi alanı + Daha iyi karakter görünürlüğü için alanlarda kullanılan yazı tipini değiştirin From c16ded1377bb630cbc1f85bef4d403b096a9f67f Mon Sep 17 00:00:00 2001 From: wellinkstein Date: Sun, 9 Dec 2018 15:11:09 +0000 Subject: [PATCH 031/289] Translated using Weblate (French) Currently translated at 98.6% (340 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f81ce7fe5..9456ac1aa 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -81,8 +81,8 @@ Pas de mémoire pour charger l\'ensemble de votre base de données. Au moins un type de génération de mot de passe doit être sélectionné. Les mots de passe ne correspondent pas. - \"Niveaux\" doit être un nombre. - \"Niveaux\" trop gros. Paramètres à 2147483648. + Faites de \"Niveaux\" un nombre. + \"Niveaux\" trop haut. Réglé à 2147483648. Un nom de champ est requis pour chaque élément. Ajoutez un titre. Entrez un entier positif dans le champ \"Longueur\". @@ -152,7 +152,7 @@ Algorithme de chiffrement de la base de données utilisé pour toutes les données. Afin de générer la clé pour l\'algorithme de chiffrement, la clé maîtresse est transformée en utilisant une fonction de dérivation de clé salée aléatoirement. Tours de transformation - Un niveau de chiffrement supérieur assure une protection supplémentaire contre les attaques de force brute, mais peut considérablement ralentir l\'ouverture et l\'enregistrement. + Un niveau de chiffrement supplémentaire assure une protection plus élevée contre les attaques de force brute, mais peut considérablement ralentir l\'ouverture et l\'enregistrement. niveaux Utilisation de la mémoire Quantité de mémoire (en octets binaires) à utiliser par la fonction de dérivation de clé. From 15fa114742ffae24124c971a78fa1ca38a158ce9 Mon Sep 17 00:00:00 2001 From: Mesut Akcan Date: Sun, 9 Dec 2018 17:20:09 +0000 Subject: [PATCH 032/289] Translated using Weblate (Turkish) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 133 ++++++++++++++++++++++++- 1 file changed, 132 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 8e6310a20..29fc4a86a 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -239,4 +239,135 @@ Eylem, harici depolama izni olmadan gerçekleştirilemez. Yazı tipi alanı Daha iyi karakter görünürlüğü için alanlarda kullanılan yazı tipini değiştirin - + Seçerek dosyaları aç + Dosya tarayıcısında seçildiğinde dosyaları otomatik aç + Pano güveni + Giriş parolası ve korunan alanların panoya aktarılmasına izin ver + UYARI: Pano tüm uygulamalar tarafından paylaşılmaktadır. Hassas veriler kopyalanırsa, diğer yazılımlar onu alabilir. + UYARI: Bu özelliği kapatmak, veritabanlarını açmayı veya kaydetmeyi engelleyebilir. + Açılacak veritabanı dosyasının bağlantısı + Veritabanı adı + Veritabanı açıklaması + Veritabanı sürümü + Metin + Uygulama + Diğer + + Klavye + Magikeyboard + Parolalarınızı ve tüm kimlik alanlarınızı içeren özel bir klavye etkinleştirin + Magikeyboard ayarları + Formları güvenli bir şekilde otomatik doldurmak için klavyeyi ayarlayın. + Cihaz ayarlarında \"Magikeyboard\"u etkinleştirin. + \"Ayarlar\" → \"Dil & giriş\" → \"Geçerli Klavye\" ve birini seçin. + veya (\"Ayarlar\" → \"Dil & giriş\" → \"Sanal klavye\" ve birini seçin.) + Bir formu doldurmanız gerektiğinde Magikeyboard\'u seçin. + Klavyenizdeki boşluk çubuğuna uzun basarak veya mevcut değilse şunu yaparak klavyeleri değiştirin: + Anahtarlı girişinizi seçin. + Giriş öğelerini kullanarak alanlarınızı doldurun. + Veritabanını kilitle. + Varsayılan klavyeyi tekrar kullan. + + Magikeyboard + Magikeyboard (KeePass DX) + Magikeyboard ayarları + + Girdi + + Zaman aşımı + Klavye girişini temizlemek için zaman aşımı + + Bildirim bilgisi + Bir giriş mevcut olduğunda bir bildirim göster + Girdi + Magikeyboard\'da %1$s mevcut + %1$s + + Kapanışta temizle + Bildirimi kapatırken klavye girişini temizle + Görünüm + + Klavye teması + + Anahtarlar + Tuşa basıldığında titreştir + Tuşa basıldığında ses çıkar + Parola olmamasına izin ver + Parola tanımlaması seçilmemişse \"Aç\" düğmesini etkinleştir + Yazma korumalı + Veritabanınızı varsayılan olarak salt okunur açın + + Eğitim ekranları + Uygulamanın nasıl çalıştığını öğrenmek için öğeleri vurgulayın + Eğitim ekranlarını sıfırla + Tüm eğitim öğelerini tekrar göster + Eğitim ekranlarını sıfırla + Veritabanı dosyanızı oluşturun + İlk parola yönetim dosyanızı oluşturun. + Mevcut bir veritabanını aç + Kullanmaya devam etmek için önceki veritabanı dosyanızı dosya tarayıcınızdan açın. + Dosyanızın konumuna bir bağlantı yeterlidir + Veritabanınızı fiziksel bir bağlantıyla da açabilirsiniz (örneğin file:// ve content:// ile). + Veritabanınıza öğe ekleyin + Girdiler dijital kimliğinizi yönetmenize yardımcı olur. +\n +\nGruplar (~ klasörler) veritabanınızdaki girdileri düzenler. + Girişlerde ara + Parolanızı kurtarmak için başlık, kullanıcı adı veya diğer alanların içeriğini girin. + Parmak iziyle veritabanı kilidini açma + Veritabanınızı hızlıca açmak için parolanızı taranan parmak izinize bağlayın. + Girdiyi düzenle + Girdinizi özel alanlarla düzenleyin. Havuz verileri farklı giriş alanları arasında referans alınabilir. + Girdiniz için güçlü bir parola oluşturun. + Girişinizle ilişkilendirmek için güçlü bir parola oluşturun, formun ölçütlerine göre kolayca tanımlayın ve güvenli parola almayı unutmayın. + Özel alanlar ekle + Ayrıca koruyabileceğiniz yeni bir formu doldurarak temel bir tedarik edilmemiş alanı kaydedin. + Veritabanınızın kilidini açın + Veritabanınızın kilidini açmak için parola ve/veya anahtar dosya girin. +\n +\nHer değişiklikten sonra veritabanı dosyanızı güvenli bir yerde yedekleyin. + Veritabanınızı yazmaya karşı koru + Oturum için açılış modunu değiştir. +\n +\n\"Yazma korumalı\", veritabanında istenmeyen değişiklikleri önler. +\n\"Değiştirilebilir\", tüm öğeleri eklemenizi, silmenizi veya değiştirmenizi sağlar. + Bir alan kopyala + Kopyalanan alanlar herhangi bir yere yapıştırılabilir. +\n +\nTercih ettiğiniz form doldurma yöntemini kullanın. + Veritabanını kilitle + Veritabanınızı hızlıca kilitleyin, uygulamayı bir süre sonra kilitlemek için ve ekran kapandığında ayarlayabilirsiniz. + Öğe sıralama + Girdilerin ve grupların nasıl sıralandığını seçin. + Katıl + Daha fazla özellik ekleyerek istikrarı, güvenliği artırmaya yardımcı olun. + + Birçok şifre yönetimi uygulamasından farklı olarak, bu, ad-free, copylefted libre yazılımı şeklindedir ve hangi sürümü kullanıyor olursanız olun, kendi sunucularında kişisel verileri toplamaz. + Profesyonel sürümü satın alarak, bu görsel özelliğe erişebilecek ve özellikle topluluk projelerinin gerçekleştirilmesine yardımcı olacaksınız + Bu görsel özellik, cömertliğiniz sayesinde kullanılabilir. + Özgürlüğümüzü korumak ve daima aktif olmak için katkılarınıza güveniyoruz + + Bu özellik geliştirme aşamasındadır ve katkılarınızın yakında kullanıma sunulmasını gerektirir. + Pro sürümünü satın alarak, + Katkıda bulunarak, + Geliştiricilerin yeni özellikler oluşturmasını ve söz konusu hatalara göre hataları düzeltmesini teşvik ediyorsunuz. + Katkınız için çok teşekkür ederim. + Bu özelliği çabucak yayınlamak için çok çalışıyoruz. + Yeni sürümleri yükleyerek uygulamanızı güncel tutmayı unutmayın. + + İndir + Katkıda bulun + + Rijndael (AES) + Twofish + ChaCha20 + + AES KDF + Argon2 + + Uygulama teması + Uygulamada kullanılan tema + Simge paketi + Uygulamada kullanılan simge paketi + + From 6372099cb2c3d9f6c4556c1bd5f0d8c82394f375 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 17 Dec 2018 16:04:35 +0100 Subject: [PATCH 033/289] Add Kotlin dependency --- app/build.gradle | 3 +++ build.gradle | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index c6f8d334e..25ef83ac2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 27 @@ -81,6 +83,7 @@ def spongycastleVersion = "1.58.0.0" def permissionDispatcherVersion = "3.1.0" dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "com.android.support:appcompat-v7:$supportVersion" implementation "com.android.support:design:$supportVersion" implementation "com.android.support:preference-v7:$supportVersion" diff --git a/build.gradle b/build.gradle index 4b688ce6c..27693ce68 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.3.11' repositories { jcenter() maven { @@ -10,6 +11,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } From cbc8ec9880ddbae7b0bf01dfcd695ec69e0065ae Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 17 Dec 2018 18:25:49 +0100 Subject: [PATCH 034/289] Refactor Lock with Kotlin --- .../keepass/activities/EntryActivity.java | 14 +- .../keepass/activities/EntryEditActivity.java | 26 +-- .../keepass/activities/GroupActivity.java | 110 ++++++------ .../keepass/lock/LockingActivity.java | 163 ------------------ .../kunzisoft/keepass/lock/LockingActivity.kt | 145 ++++++++++++++++ .../keepass/settings/SettingsActivity.java | 5 +- .../kunzisoft/keepass/timeout/Timeout.java | 78 --------- .../keepass/timeout/TimeoutHelper.java | 91 ---------- .../keepass/timeout/TimeoutHelper.kt | 145 ++++++++++++++++ app/src/main/res/values/donottranslate.xml | 2 +- 10 files changed, 367 insertions(+), 412 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/timeout/Timeout.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 779cd1205..e3ec1354f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -47,13 +47,13 @@ import com.kunzisoft.keepass.database.ExtraFields; import com.kunzisoft.keepass.database.PwDatabase; import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.lock.LockingActivity; import com.kunzisoft.keepass.lock.LockingHideActivity; import com.kunzisoft.keepass.notifications.NotificationCopyingService; import com.kunzisoft.keepass.notifications.NotificationField; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.settings.SettingsAutofillActivity; import com.kunzisoft.keepass.timeout.ClipboardHelper; +import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.Types; @@ -85,12 +85,12 @@ public class EntryActivity extends LockingHideActivity { private int iconColor; - public static void launch(Activity act, PwEntry pw, boolean readOnly) { - if (LockingActivity.checkTimeIsAllowedOrFinish(act)) { - Intent intent = new Intent(act, EntryActivity.class); + public static void launch(Activity activity, PwEntry pw, boolean readOnly) { + if (TimeoutHelper.INSTANCE.checkTime(activity)) { + Intent intent = new Intent(activity, EntryActivity.class); intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID())); ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly); - act.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE); + activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE); } } @@ -113,7 +113,7 @@ public class EntryActivity extends LockingHideActivity { finish(); return; } - readOnly = db.isReadOnly() || readOnly; + setReadOnly(db.isReadOnly() || getReadOnly()); mShowPassword = !PreferencesUtil.isPasswordMask(this); @@ -428,7 +428,7 @@ public class EntryActivity extends LockingHideActivity { inflater.inflate(R.menu.entry, menu); inflater.inflate(R.menu.database_lock, menu); - if (readOnly) { + if (getReadOnly()) { MenuItem edit = menu.findItem(R.id.menu_edit); if (edit != null) edit.setVisible(false); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 8f21d8d51..99c69fdc1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -56,11 +56,11 @@ import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; -import com.kunzisoft.keepass.lock.LockingActivity; import com.kunzisoft.keepass.lock.LockingHideActivity; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; +import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.Types; import com.kunzisoft.keepass.utils.Util; @@ -113,28 +113,28 @@ public class EntryEditActivity extends LockingHideActivity /** * Launch EntryEditActivity to update an existing entry * - * @param act from activity - * @param pw Entry to update + * @param activity from activity + * @param pwEntry Entry to update */ - public static void launch(Activity act, PwEntry pw) { - if (LockingActivity.checkTimeIsAllowedOrFinish(act)) { - Intent intent = new Intent(act, EntryEditActivity.class); - intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID())); - act.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); + public static void launch(Activity activity, PwEntry pwEntry) { + if (TimeoutHelper.INSTANCE.checkTime(activity)) { + Intent intent = new Intent(activity, EntryEditActivity.class); + intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pwEntry.getUUID())); + activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); } } /** * Launch EntryEditActivity to add a new entry * - * @param act from activity + * @param activity from activity * @param pwGroup Group who will contains new entry */ - public static void launch(Activity act, PwGroup pwGroup) { - if (LockingActivity.checkTimeIsAllowedOrFinish(act)) { - Intent intent = new Intent(act, EntryEditActivity.class); + public static void launch(Activity activity, PwGroup pwGroup) { + if (TimeoutHelper.INSTANCE.checkTime(activity)) { + Intent intent = new Intent(activity, EntryEditActivity.class); intent.putExtra(KEY_PARENT, pwGroup.getId()); - act.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); + activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 0868a03d2..8a9f663b3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -87,6 +87,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; import com.kunzisoft.keepass.tasks.UIToastTask; import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; +import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.view.AddNodeButtonView; @@ -144,13 +145,13 @@ public class GroupActivity extends LockingActivity } public static void launch(Activity act, boolean readOnly) { - startRecordTime(act); + TimeoutHelper.INSTANCE.recordTime(act); launch(act, null, readOnly); } private static void buildAndLaunchIntent(Activity activity, PwGroup group, boolean readOnly, IntentBuildLauncher intentBuildLauncher) { - if (checkTimeIsAllowedOrFinish(activity)) { + if (TimeoutHelper.INSTANCE.checkTime(activity)) { Intent intent = new Intent(activity, GroupActivity.class); if (group != null) { intent.putExtra(GROUP_ID_KEY, group.getId()); @@ -165,9 +166,9 @@ public class GroupActivity extends LockingActivity (intent) -> activity.startActivityForResult(intent, 0)); } - public static void launchForKeyboardResult(Activity act, boolean readOnly) { - startRecordTime(act); - launchForKeyboardResult(act, null, readOnly); + public static void launchForKeyboardResult(Activity activity, boolean readOnly) { + TimeoutHelper.INSTANCE.recordTime(activity); + launchForKeyboardResult(activity, null, readOnly); } public static void launchForKeyboardResult(Activity activity, PwGroup group, boolean readOnly) { @@ -179,12 +180,12 @@ public class GroupActivity extends LockingActivity } @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity act, AssistStructure assistStructure, boolean readOnly) { + public static void launchForAutofillResult(Activity activity, AssistStructure assistStructure, boolean readOnly) { if ( assistStructure != null ) { - startRecordTime(act); - launchForAutofillResult(act, null, assistStructure, readOnly); + TimeoutHelper.INSTANCE.recordTime(activity); + launchForAutofillResult(activity, null, assistStructure, readOnly); } else { - launch(act, readOnly); + launch(activity, readOnly); } } @@ -231,7 +232,7 @@ public class GroupActivity extends LockingActivity invalidateOptionsMenu(); // Get arg from intent or instance state - readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, getIntent()); + setReadOnly(ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, getIntent())); // Retrieve elements after an orientation change if (savedInstanceState != null) { @@ -282,7 +283,7 @@ public class GroupActivity extends LockingActivity listNodesFragment = (ListNodesFragment) getSupportFragmentManager() .findFragmentByTag(fragmentTag); if (listNodesFragment == null) - listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly, currentGroupIsASearch); + listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, getReadOnly(), currentGroupIsASearch); // Attach fragment to content view getSupportFragmentManager().beginTransaction().replace( @@ -340,35 +341,34 @@ public class GroupActivity extends LockingActivity } private void openGroup(PwGroup group, boolean isASearch) { - // Check Timeout - if (checkTimeIsAllowedOrFinish(this)) { - startRecordTime(this); + // Check TimeoutHelper + TimeoutHelper.INSTANCE.touchToReinitTime(this, () -> { + // Open a group in a new fragment + ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, getReadOnly(), isASearch); + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + // Different animation + String fragmentTag; + if (isASearch) { + fragmentTransaction.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom, + R.anim.slide_in_bottom, R.anim.slide_out_top); + fragmentTag = SEARCH_FRAGMENT_TAG; + } else { + fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, + R.anim.slide_in_left, R.anim.slide_out_right); + fragmentTag = LIST_NODES_FRAGMENT_TAG; + } - // Open a group in a new fragment - ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, readOnly, isASearch); - FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); - // Different animation - String fragmentTag; - if (isASearch) { - fragmentTransaction.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom, - R.anim.slide_in_bottom, R.anim.slide_out_top); - fragmentTag = SEARCH_FRAGMENT_TAG; - } else { - fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, - R.anim.slide_in_left, R.anim.slide_out_right); - fragmentTag = LIST_NODES_FRAGMENT_TAG; - } + fragmentTransaction.replace(R.id.nodes_list_fragment_container, + newListNodeFragment, + fragmentTag); + fragmentTransaction.addToBackStack(fragmentTag); + fragmentTransaction.commit(); - fragmentTransaction.replace(R.id.nodes_list_fragment_container, - newListNodeFragment, - fragmentTag); - fragmentTransaction.addToBackStack(fragmentTag); - fragmentTransaction.commit(); - - listNodesFragment = newListNodeFragment; - mCurrentGroup = group; - assignGroupViewElements(); - } + listNodesFragment = newListNodeFragment; + mCurrentGroup = group; + assignGroupViewElements(); + return null; + }); } @Override @@ -380,7 +380,7 @@ public class GroupActivity extends LockingActivity outState.putParcelable(NODE_TO_COPY_KEY, nodeToCopy); if (nodeToMove != null) outState.putParcelable(NODE_TO_MOVE_KEY, nodeToMove); - ReadOnlyHelper.onSaveInstanceState(outState, readOnly); + ReadOnlyHelper.onSaveInstanceState(outState, getReadOnly()); super.onSaveInstanceState(outState); } @@ -401,7 +401,7 @@ public class GroupActivity extends LockingActivity pwGroupId = intent.getParcelableExtra(GROUP_ID_KEY); } - readOnly = database.isReadOnly() || readOnly; // Force read only if the database is like that + setReadOnly(database.isReadOnly() || getReadOnly()); // Force read only if the database is like that Log.w(TAG, "Creating tree view"); PwGroup currentGroup; @@ -463,8 +463,8 @@ public class GroupActivity extends LockingActivity if (addNodeButtonView != null) { // To enable add button - boolean addGroupEnabled = !readOnly && !currentGroupIsASearch; - boolean addEntryEnabled = !readOnly && !currentGroupIsASearch; + boolean addGroupEnabled = !getReadOnly() && !currentGroupIsASearch; + boolean addEntryEnabled = !getReadOnly() && !currentGroupIsASearch; if (mCurrentGroup != null) { boolean isRoot = (mCurrentGroup == rootGroup); if (!mCurrentGroup.allowAddEntryIfIsRoot()) @@ -524,7 +524,7 @@ public class GroupActivity extends LockingActivity openChildGroup((PwGroup) node); break; case ENTRY: - EntryActivity.launch(this, (PwEntry) node, readOnly); + EntryActivity.launch(this, (PwEntry) node, getReadOnly()); break; } } @@ -858,7 +858,7 @@ public class GroupActivity extends LockingActivity MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.search, menu); - if (!readOnly) + if (!getReadOnly()) inflater.inflate(R.menu.database_master_key, menu); inflater.inflate(R.menu.database_lock, menu); @@ -945,7 +945,7 @@ public class GroupActivity extends LockingActivity return true; case R.id.menu_lock: - lockAndExit(); + lockAndExit(); return true; case R.id.menu_change_master_key: @@ -953,7 +953,7 @@ public class GroupActivity extends LockingActivity return true; default: // Check the time lock before launching settings - MenuUtil.onDefaultMenuOptionsItemSelected(this, item, readOnly, true); + MenuUtil.onDefaultMenuOptionsItemSelected(this, item, getReadOnly(), true); return super.onOptionsItemSelected(item); } } @@ -1197,17 +1197,13 @@ public class GroupActivity extends LockingActivity @Override public void onBackPressed() { - if (checkTimeIsAllowedOrFinish(this)) { - startRecordTime(this); + super.onBackPressed(); - super.onBackPressed(); - - listNodesFragment = (ListNodesFragment) getSupportFragmentManager().findFragmentByTag(LIST_NODES_FRAGMENT_TAG); - // to refresh fragment - listNodesFragment.rebuildList(); - mCurrentGroup = listNodesFragment.getMainGroup(); - removeSearchInIntent(getIntent()); - assignGroupViewElements(); - } + listNodesFragment = (ListNodesFragment) getSupportFragmentManager().findFragmentByTag(LIST_NODES_FRAGMENT_TAG); + // to refresh fragment + listNodesFragment.rebuildList(); + mCurrentGroup = listNodesFragment.getMainGroup(); + removeSearchInIntent(getIntent()); + assignGroupViewElements(); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.java b/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.java deleted file mode 100644 index ea4646c6f..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.lock; - -import android.app.Activity; -import android.app.NotificationManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.util.Log; - -import com.kunzisoft.keepass.activities.ReadOnlyHelper; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.stylish.StylishActivity; -import com.kunzisoft.keepass.timeout.TimeoutHelper; - -public abstract class LockingActivity extends StylishActivity { - - private static final String TAG = LockingActivity.class.getName(); - - public static final String LOCK_ACTION = "com.kunzisoft.keepass.LOCK"; - - public static final int RESULT_EXIT_LOCK = 1450; - - private LockReceiver lockReceiver; - private boolean exitLock; - - protected boolean readOnly; - - /** - * Called to start a record time, - * Generally used for a first launch or for a fragment change - */ - protected static void startRecordTime(Activity activity) { - TimeoutHelper.recordTime(activity); - } - - protected static boolean checkTimeIsAllowedOrFinish(Activity activity) { - return TimeoutHelper.checkTime(activity); - } - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this)) { - lockReceiver = new LockReceiver(); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_SCREEN_OFF); - intentFilter.addAction(LOCK_ACTION); - registerReceiver(lockReceiver, new IntentFilter(intentFilter)); - } else - lockReceiver = null; - - exitLock = false; - - readOnly = false; - readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, getIntent()); - } - - public static void checkShutdown(Activity activity) { - if (App.isShutdown() && App.getDB().getLoaded()) { - lockAndExit(activity); - } - } - - private static void lockAndExit(Activity activity) { - App.setShutdown(); - Log.i(TAG, "Shutdown " + activity.getLocalClassName() + - " after inactivity or manual lock"); - NotificationManager nm = (NotificationManager) activity.getSystemService(NOTIFICATION_SERVICE); - if (nm != null) - nm.cancelAll(); - activity.setResult(LockingActivity.RESULT_EXIT_LOCK); - activity.finish(); - } - - protected void lockAndExit() { - lockAndExit(this); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == RESULT_EXIT_LOCK) { - exitLock = true; - checkShutdown(this); - } - } - - @Override - protected void onResume() { - super.onResume(); - // After the first creation - // or If simply swipe with another application - // If the time is out -> close the Activity - TimeoutHelper.checkTime(this); - // If onCreate already record time - if (!exitLock) - TimeoutHelper.recordTime(this); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - ReadOnlyHelper.onSaveInstanceState(outState, readOnly); - super.onSaveInstanceState(outState); - } - - @Override - protected void onPause() { - super.onPause(); - // If the time is out during our navigation in activity -> close the Activity - TimeoutHelper.checkTime(this); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if(lockReceiver != null) - unregisterReceiver(lockReceiver); - } - - public class LockReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if(action != null) { - switch (action) { - case Intent.ACTION_SCREEN_OFF: - if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(LockingActivity.this)) { - lockAndExit(); - } - break; - case LOCK_ACTION: - lockAndExit(); - break; - } - } - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.kt new file mode 100644 index 000000000..95276d07f --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.lock + +import android.app.Activity +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import android.util.Log +import com.kunzisoft.keepass.activities.ReadOnlyHelper +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.stylish.StylishActivity +import com.kunzisoft.keepass.timeout.TimeoutHelper + +abstract class LockingActivity : StylishActivity() { + + companion object { + + const val LOCK_ACTION = "com.kunzisoft.keepass.LOCK" + + const val RESULT_EXIT_LOCK = 1450 + } + + private var lockReceiver: LockReceiver? = null + private var exitLock: Boolean = false + + protected var readOnly: Boolean = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this)) { + lockReceiver = LockReceiver() + val intentFilter = IntentFilter() + intentFilter.addAction(Intent.ACTION_SCREEN_OFF) + intentFilter.addAction(LOCK_ACTION) + registerReceiver(lockReceiver, IntentFilter(intentFilter)) + } else + lockReceiver = null + + exitLock = false + + readOnly = false + readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, intent) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == RESULT_EXIT_LOCK) { + exitLock = true + checkShutdown() + } + } + + override fun onResume() { + super.onResume() + // After the first creation + // or If simply swipe with another application + // If the time is out -> close the Activity + TimeoutHelper.checkTime(this) + // If onCreate already record time + if (!exitLock) + TimeoutHelper.recordTime(this) + } + + override fun onSaveInstanceState(outState: Bundle) { + ReadOnlyHelper.onSaveInstanceState(outState, readOnly) + super.onSaveInstanceState(outState) + } + + override fun onPause() { + super.onPause() + // If the time is out during our navigation in activity -> close the Activity + TimeoutHelper.checkTime(this) + } + + override fun onDestroy() { + super.onDestroy() + if (lockReceiver != null) + unregisterReceiver(lockReceiver) + } + + inner class LockReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context, intent: Intent) { + val action = intent.action + if (action != null) { + when (action) { + Intent.ACTION_SCREEN_OFF -> if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this@LockingActivity)) { + lockAndExit() + } + LOCK_ACTION -> lockAndExit() + } + } + } + } + + protected fun lockAndExit() { + lock() + } + + private fun checkShutdown() { + if (App.isShutdown() && App.getDB().loaded) { + lockAndExit() + } + } + + override fun onBackPressed() { + TimeoutHelper.touchToReinitTime(this) { + super.onBackPressed() + } + } +} + +fun Activity.lock() { + App.setShutdown() + Log.i(Activity::class.java.name, "Shutdown " + localClassName + + " after inactivity or manual lock") + (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).apply { + cancelAll() + } + setResult(LockingActivity.RESULT_EXIT_LOCK) + finish() +} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java index 8a48e4fa6..6407d4901 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java @@ -30,6 +30,7 @@ import android.view.MenuItem; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.ReadOnlyHelper; import com.kunzisoft.keepass.lock.LockingActivity; +import com.kunzisoft.keepass.timeout.TimeoutHelper; public class SettingsActivity extends LockingActivity implements MainPreferenceFragment.Callback { @@ -50,7 +51,7 @@ public class SettingsActivity extends LockingActivity implements MainPreferenceF // To avoid flickering when launch settings in a LockingActivity if (!checkLock) launch(activity, readOnly); - else if (LockingActivity.checkTimeIsAllowedOrFinish(activity)) { + else if (TimeoutHelper.INSTANCE.checkTime(activity)) { launch(activity, readOnly); } } @@ -116,7 +117,7 @@ public class SettingsActivity extends LockingActivity implements MainPreferenceF getSupportFragmentManager().beginTransaction() .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right) - .replace(R.id.fragment_container, NestedSettingsFragment.newInstance(key, readOnly), TAG_NESTED) + .replace(R.id.fragment_container, NestedSettingsFragment.newInstance(key, getReadOnly()), TAG_NESTED) .addToBackStack(TAG_NESTED) .commit(); diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/Timeout.java b/app/src/main/java/com/kunzisoft/keepass/timeout/Timeout.java deleted file mode 100644 index 08b022d55..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/Timeout.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.timeout; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.util.Log; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.lock.LockingActivity; - -public class Timeout { - - private static final int REQUEST_ID = 0; - private static final long DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes - private static String TAG = "KeePass Timeout"; - - private static PendingIntent buildIntent(Context ctx) { - Intent intent = new Intent(LockingActivity.LOCK_ACTION); - return PendingIntent.getBroadcast(ctx, REQUEST_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT); - } - - public static void start(Context ctx) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - String sTimeout = prefs.getString(ctx.getString(R.string.app_timeout_key), ctx.getString(R.string.clipboard_timeout_default)); - - long timeout; - try { - timeout = Long.parseLong(sTimeout); - } catch (NumberFormatException e) { - timeout = DEFAULT_TIMEOUT; - } - - if ( timeout == -1 ) { - // No timeout don't start timeout service - return; - } - - long triggerTime = System.currentTimeMillis() + timeout; - AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); - - Log.d(TAG, "Timeout start"); - if (am != null) { - am.set(AlarmManager.RTC, triggerTime, buildIntent(ctx)); - } - } - - public static void cancel(Context ctx) { - AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); - - Log.d(TAG, "Timeout cancel"); - if (am != null) { - am.cancel(buildIntent(ctx)); - } - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.java b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.java deleted file mode 100644 index f37c07bd1..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.timeout; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.lock.LockingActivity; -import com.kunzisoft.keepass.app.App; - -public class TimeoutHelper { - - private static final String TAG = "TimeoutHelper"; - - public static final long DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes - public static final long TIMEOUT_NEVER = -1; // Infinite - - public static void recordTime(Activity act) { - // Record timeout time in case timeout service is killed - long time = System.currentTimeMillis(); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(act); - SharedPreferences.Editor edit = prefs.edit(); - edit.putLong(act.getString(R.string.timeout_key), time); - edit.apply(); - - if ( App.getDB().getLoaded() ) { - Timeout.start(act); - } - } - - public static boolean checkTime(Activity act) { - if ( App.getDB().getLoaded() ) { - Timeout.cancel(act); - } - - // Check whether the timeout has expired - long cur_time = System.currentTimeMillis(); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(act); - long timeout_start = prefs.getLong(act.getString(R.string.timeout_key), TIMEOUT_NEVER); - // The timeout never started - if (timeout_start == TIMEOUT_NEVER) { - return true; - } - - String sTimeout = prefs.getString(act.getString(R.string.app_timeout_key), act.getString(R.string.clipboard_timeout_default)); - long timeout; - try { - timeout = Long.parseLong(sTimeout); - } catch (NumberFormatException e) { - timeout = DEFAULT_TIMEOUT; - } - - // We are set to never timeout - if (timeout == TIMEOUT_NEVER) { - return true; - } - - long diff = cur_time - timeout_start; - if (diff >= timeout) { - // We have timed out - if ( App.getDB().getLoaded() ) { - App.setShutdown(act.getString(R.string.app_timeout)); - LockingActivity.checkShutdown(act); - return false; - } - } - return true; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt new file mode 100644 index 000000000..97f4f0a29 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -0,0 +1,145 @@ +/* + * Copyright 2018 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.timeout + +import android.app.Activity +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.preference.PreferenceManager +import android.util.Log +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.lock.LockingActivity +import com.kunzisoft.keepass.lock.lock + +object TimeoutHelper { + + const val DEFAULT_TIMEOUT = (5 * 60 * 1000).toLong() // 5 minutes + const val TIMEOUT_NEVER: Long = -1 // Infinite + + private const val REQUEST_ID = 140 + + private const val TAG = "TimeoutHelper" + + private fun getLockPendingIntent(context: Context): PendingIntent { + return PendingIntent.getBroadcast(context, + REQUEST_ID, + Intent(LockingActivity.LOCK_ACTION), + PendingIntent.FLAG_CANCEL_CURRENT) + } + + /** + * Record the current time to check it later with checkTime + */ + fun recordTime(context: Context) { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + + // Record timeout time in case timeout service is killed + val time = System.currentTimeMillis() + val edit = prefs.edit() + edit.putLong(context.getString(R.string.timeout_backup_key), time) + edit.apply() + + if (App.getDB().loaded) { + val timeout = try { + java.lang.Long.parseLong(prefs.getString(context.getString(R.string.app_timeout_key), + context.getString(R.string.clipboard_timeout_default))) + } catch (e: NumberFormatException) { + DEFAULT_TIMEOUT + } + + // No timeout don't start timeout service + if (timeout != TIMEOUT_NEVER) { + val triggerTime = System.currentTimeMillis() + timeout + val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + Log.d(TAG, "TimeoutHelper start") + am.set(AlarmManager.RTC, triggerTime, getLockPendingIntent(context)) + } + } + } + + /** + * Check the time previously record with recordTime and lock the activity if timeout + */ + fun checkTime(activity: Activity): Boolean { + return checkTime(activity) { + if (App.isShutdown() && App.getDB().loaded) + activity.lock() + } + } + + /** + * Check the time previously record with recordTime and do the shutdown action if timeout + */ + fun checkTime(context: Context, shutdown: (() -> Unit)): Boolean { + // Cancel the lock PendingIntent + if (App.getDB().loaded) { + val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + Log.d(TAG, "TimeoutHelper cancel") + am.cancel(getLockPendingIntent(context)) + } + + // Check whether the timeout has expired + val currentTime = System.currentTimeMillis() + + // Retrieve the timeout programmatically backup + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val timeoutBackup = prefs.getLong(context.getString(R.string.timeout_backup_key), + TIMEOUT_NEVER) + // The timeout never started + if (timeoutBackup == TIMEOUT_NEVER) { + return true + } + + // Retrieve the app timeout in settings + val appTimeout = try { + java.lang.Long.parseLong(prefs.getString(context.getString(R.string.app_timeout_key), + context.getString(R.string.clipboard_timeout_default))) + } catch (e: NumberFormatException) { + DEFAULT_TIMEOUT + } + + // We are set to never timeout + if (appTimeout == TIMEOUT_NEVER) { + return true + } + + // See if not a timeout + val diff = currentTime - timeoutBackup + if (diff >= appTimeout) { + // We have timed out + if (App.getDB().loaded) { + App.setShutdown(context.getString(R.string.app_timeout)) + shutdown.invoke() + return false + } + } + return true + } + + fun touchToReinitTime(activity: Activity, action: (() -> Unit)? = null) { + if (checkTime(activity)) { + recordTime(activity) + action?.invoke() + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index d3906e7fb..54db9af15 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -48,6 +48,7 @@ algorithm key_derivation_function_key app + timeout_backup_key app_timeout_key clip_timeout_key db @@ -66,7 +67,6 @@ sort_group_before_key sort_ascending_key sort_recycle_bin_bottom_key - timeout_key storage_access_framework_key setting_style_key setting_icon_pack_choose_key From 8853bb761886549ffd47a88d13b29a0e1361b300 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 17 Dec 2018 19:07:24 +0100 Subject: [PATCH 035/289] Move LockingActivity --- .../keepass/activities/EntryActivity.java | 2 +- .../keepass/activities/EntryEditActivity.java | 2 +- .../keepass/activities/GroupActivity.java | 2 +- .../{ => activities}/lock/LockingActivity.kt | 2 +- .../activities/lock/LockingHideActivity.kt | 55 ++++++++++++++++++ .../keepass/lock/LockingHideActivity.java | 56 ------------------- .../keepass/password/PasswordActivity.java | 2 +- .../keepass/settings/MagikIMESettings.java | 2 +- .../keepass/settings/SettingsActivity.java | 2 +- .../keepass/timeout/TimeoutHelper.kt | 4 +- 10 files changed, 64 insertions(+), 65 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/lock/LockingActivity.kt (98%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingHideActivity.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/lock/LockingHideActivity.java diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index e3ec1354f..f6374de5d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -47,7 +47,7 @@ import com.kunzisoft.keepass.database.ExtraFields; import com.kunzisoft.keepass.database.PwDatabase; import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.lock.LockingHideActivity; +import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.notifications.NotificationCopyingService; import com.kunzisoft.keepass.notifications.NotificationField; import com.kunzisoft.keepass.settings.PreferencesUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 99c69fdc1..ad5aa7d44 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -56,7 +56,7 @@ import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; -import com.kunzisoft.keepass.lock.LockingHideActivity; +import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 8a9f663b3..0fc386e39 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -80,7 +80,7 @@ import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; import com.kunzisoft.keepass.dialogs.ReadOnlyDialog; import com.kunzisoft.keepass.dialogs.SortDialogFragment; -import com.kunzisoft.keepass.lock.LockingActivity; +import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.password.AssignPasswordHelper; import com.kunzisoft.keepass.selection.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.kt rename to app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index 95276d07f..14d43b2d4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.lock +package com.kunzisoft.keepass.activities.lock import android.app.Activity import android.app.NotificationManager diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingHideActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingHideActivity.kt new file mode 100644 index 000000000..f7da90250 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingHideActivity.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.activities.lock + +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.view.WindowManager + +/** + * Locking Hide Activity that sets FLAG_SECURE to prevent screenshots, and from + * appearing in the recent app preview + */ +abstract class LockingHideActivity : LockingActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Several gingerbread devices have problems with FLAG_SECURE + window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE) + } + + /* (non-Javadoc) Workaround for HTC Linkify issues + * @see android.app.Activity#startActivity(android.content.Intent) + */ + override fun startActivity(intent: Intent) { + try { + if (intent.component != null && intent.component!!.shortClassName == ".HtcLinkifyDispatcherActivity") { + intent.component = null + } + super.startActivity(intent) + } catch (e: ActivityNotFoundException) { + /* Catch the bad HTC implementation case */ + super.startActivity(Intent.createChooser(intent, null)) + } + + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/lock/LockingHideActivity.java b/app/src/main/java/com/kunzisoft/keepass/lock/LockingHideActivity.java deleted file mode 100644 index 6fddd6abe..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/lock/LockingHideActivity.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.lock; - -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.os.Bundle; -import android.view.WindowManager; - -/** - * Locking Hide Activity that sets FLAG_SECURE to prevent screenshots, and from - * appearing in the recent app preview - */ -public abstract class LockingHideActivity extends LockingActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Several gingerbread devices have problems with FLAG_SECURE - getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); - } - - /* (non-Javadoc) Workaround for HTC Linkify issues - * @see android.app.Activity#startActivity(android.content.Intent) - */ - @Override - public void startActivity(Intent intent) { - try { - if (intent.getComponent() != null && intent.getComponent().getShortClassName().equals(".HtcLinkifyDispatcherActivity")) { - intent.setComponent(null); - } - super.startActivity(intent); - } catch (ActivityNotFoundException e) { - /* Catch the bad HTC implementation case */ - super.startActivity(Intent.createChooser(intent, null)); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 71c246737..a570b6442 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -69,7 +69,7 @@ import com.kunzisoft.keepass.fileselect.KeyFileHelper; import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; -import com.kunzisoft.keepass.lock.LockingActivity; +import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.selection.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java index db84fd41c..72a65ac6c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java @@ -24,7 +24,7 @@ import android.support.v7.widget.Toolbar; import android.view.MenuItem; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.lock.LockingActivity; +import com.kunzisoft.keepass.activities.lock.LockingActivity; public class MagikIMESettings extends LockingActivity { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java index 6407d4901..b08be9451 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java @@ -29,7 +29,7 @@ import android.view.MenuItem; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.ReadOnlyHelper; -import com.kunzisoft.keepass.lock.LockingActivity; +import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.timeout.TimeoutHelper; diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index 97f4f0a29..3d47800c9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -28,8 +28,8 @@ import android.preference.PreferenceManager import android.util.Log import com.kunzisoft.keepass.R import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.lock.LockingActivity -import com.kunzisoft.keepass.lock.lock +import com.kunzisoft.keepass.activities.lock.LockingActivity +import com.kunzisoft.keepass.activities.lock.lock object TimeoutHelper { From 6712b0927d4f787ddcc1420e5049b9e5f32a3c5c Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 19 Dec 2018 11:57:39 +0100 Subject: [PATCH 036/289] Refactor entry views and check focus to reset timeout --- .../keepass/activities/EntryEditActivity.java | 37 ++++++++++------ .../keepass/activities/GroupActivity.java | 5 ++- .../activities/lock/LockingActivity.kt | 16 ++++++- .../dialogs/GroupEditDialogFragment.java | 4 +- .../keepass/timeout/TimeoutHelper.kt | 4 +- .../keepass/view/EntryContentsView.java | 4 +- app/src/main/res/layout/entry_edit.xml | 42 +++++++++---------- .../main/res/layout/entry_view_contents.xml | 19 +++++---- app/src/main/res/layout/group_edit.xml | 8 ++-- app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-ca/strings.xml | 2 +- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values-da/strings.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-el/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-eu/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-hu/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-iw/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-lt/strings.xml | 2 +- app/src/main/res/values-lv/strings.xml | 2 +- app/src/main/res/values-nb/strings.xml | 2 +- app/src/main/res/values-nl/strings.xml | 2 +- app/src/main/res/values-nn/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-pt-rPT/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-sk/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 38 files changed, 115 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index ad5aa7d44..b40c5f2e9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -149,17 +149,28 @@ public class EntryEditActivity extends LockingHideActivity getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); - scrollView = findViewById(R.id.entry_scroll); + scrollView = findViewById(R.id.entry_edit_scroll); scrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); - entryTitleView = findViewById(R.id.entry_title); - entryIconView = findViewById(R.id.icon_button); - entryUserNameView = findViewById(R.id.entry_user_name); - entryUrlView = findViewById(R.id.entry_url); - entryPasswordView = findViewById(R.id.entry_password); - entryConfirmationPasswordView = findViewById(R.id.entry_confpassword); - entryCommentView = findViewById(R.id.entry_comment); - entryExtraFieldsContainer = findViewById(R.id.advanced_container); + entryTitleView = findViewById(R.id.entry_edit_title); + entryIconView = findViewById(R.id.entry_edit_icon_button); + entryUserNameView = findViewById(R.id.entry_edit_user_name); + entryUrlView = findViewById(R.id.entry_edit_url); + entryPasswordView = findViewById(R.id.entry_edit_password); + entryConfirmationPasswordView = findViewById(R.id.entry_edit_confirmation_password); + entryCommentView = findViewById(R.id.entry_edit_notes); + entryExtraFieldsContainer = findViewById(R.id.entry_edit_advanced_container); + + // Focus view to reinitialize timeout + resetAppTimeoutWhenViewFocusedOrChanged( + entryTitleView, + entryIconView, + entryUserNameView, + entryUrlView, + entryPasswordView, + entryConfirmationPasswordView, + entryCommentView, + entryExtraFieldsContainer); // Likely the app has been killed exit the activity database = App.getDB(); @@ -207,16 +218,16 @@ public class EntryEditActivity extends LockingHideActivity IconPickerDialogFragment.launch(EntryEditActivity.this)); // Generate password button - generatePasswordView = findViewById(R.id.generate_button); + generatePasswordView = findViewById(R.id.entry_edit_generate_button); generatePasswordView.setOnClickListener(v -> openPasswordGenerator()); // Save button - saveView = findViewById(R.id.entry_save); + saveView = findViewById(R.id.entry_edit_save); saveView.setOnClickListener(v -> saveEntry()); if (mEntry.allowExtraFields()) { - addNewFieldView = findViewById(R.id.add_new_field); + addNewFieldView = findViewById(R.id.entry_edit_add_new_field); addNewFieldView.setVisibility(View.VISIBLE); addNewFieldView.setOnClickListener(v -> addNewCustomField()); } @@ -510,7 +521,7 @@ public class EntryEditActivity extends LockingHideActivity } if (mEntry.allowExtraFields()) { - LinearLayout container = findViewById(R.id.advanced_container); + LinearLayout container = findViewById(R.id.entry_edit_advanced_container); mEntry.getFields().doActionToAllCustomProtectedField((key, value) -> { EntryEditCustomField entryEditCustomField = new EntryEditCustomField(EntryEditActivity.this); entryEditCustomField.setData(key, value); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 0fc386e39..7a36a9b71 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -229,6 +229,9 @@ public class GroupActivity extends LockingActivity toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout); toolbarPaste = findViewById(R.id.toolbar_paste); + // Focus view to reinitialize timeout + resetAppTimeoutWhenViewFocusedOrChanged(addNodeButtonView); + invalidateOptionsMenu(); // Get arg from intent or instance state @@ -342,7 +345,7 @@ public class GroupActivity extends LockingActivity private void openGroup(PwGroup group, boolean isASearch) { // Check TimeoutHelper - TimeoutHelper.INSTANCE.touchToReinitTime(this, () -> { + TimeoutHelper.INSTANCE.resetTime(this, () -> { // Open a group in a new fragment ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, getReadOnly(), isASearch); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index 14d43b2d4..62249797b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -27,6 +27,7 @@ import android.content.Intent import android.content.IntentFilter import android.os.Bundle import android.util.Log +import android.view.View import com.kunzisoft.keepass.activities.ReadOnlyHelper import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.settings.PreferencesUtil @@ -126,8 +127,21 @@ abstract class LockingActivity : StylishActivity() { } } + /** + * To reset the app timeout when a view is focused or changed + */ + protected fun resetAppTimeoutWhenViewFocusedOrChanged(vararg views: View) { + views.forEach { + it.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + TimeoutHelper.resetTime(this) + } + } + } + } + override fun onBackPressed() { - TimeoutHelper.touchToReinitTime(this) { + TimeoutHelper.resetTime(this) { super.onBackPressed() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index 3e58ccb71..f78f0eaa3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -109,8 +109,8 @@ public class GroupEditDialogFragment extends DialogFragment assert getActivity() != null; LayoutInflater inflater = getActivity().getLayoutInflater(); View root = inflater.inflate(R.layout.group_edit, null); - TextView nameField = root.findViewById(R.id.group_name); - iconButton = root.findViewById(R.id.icon_button); + TextView nameField = root.findViewById(R.id.group_edit_name); + iconButton = root.findViewById(R.id.group_edit_icon_button); // Retrieve the textColor to tint the icon int[] attrs = {android.R.attr.textColorPrimary}; diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index 3d47800c9..bfd9a8044 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -27,9 +27,9 @@ import android.content.Intent import android.preference.PreferenceManager import android.util.Log import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.lock +import com.kunzisoft.keepass.app.App object TimeoutHelper { @@ -136,7 +136,7 @@ object TimeoutHelper { return true } - fun touchToReinitTime(activity: Activity, action: (() -> Unit)? = null) { + fun resetTime(activity: Activity, action: (() -> Unit)? = null) { if (checkTime(activity)) { recordTime(activity) action?.invoke() diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java index e6a299e4d..6da530de5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java @@ -104,8 +104,8 @@ public class EntryContentsView extends LinearLayout { urlContainerView = findViewById(R.id.entry_url_container); urlView = findViewById(R.id.entry_url); - commentContainerView = findViewById(R.id.entry_comment_container); - commentView = findViewById(R.id.entry_comment); + commentContainerView = findViewById(R.id.entry_notes_container); + commentView = findViewById(R.id.entry_notes); extrasView = findViewById(R.id.extra_strings); diff --git a/app/src/main/res/layout/entry_edit.xml b/app/src/main/res/layout/entry_edit.xml index 5b0f536b6..0fcb9393b 100644 --- a/app/src/main/res/layout/entry_edit.xml +++ b/app/src/main/res/layout/entry_edit.xml @@ -30,7 +30,7 @@ layout="@layout/toolbar_default" /> + android:layout_toLeftOf="@+id/entry_edit_icon_button" + android:layout_toStartOf="@+id/entry_edit_icon_button"> + android:layout_toLeftOf="@+id/entry_edit_generate_button" + android:layout_toStartOf="@+id/entry_edit_generate_button"> + android:layout_toLeftOf="@+id/entry_edit_generate_button" + android:layout_toStartOf="@+id/entry_edit_generate_button" + android:layout_below="@+id/entry_edit_container_password"> + android:hint="@string/entry_notes" /> . --> - - - - - diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index f95c06d05..11ff5a077 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -38,7 +38,7 @@ فك تعمية محتويات قاعدة البيانات … أرقام إلغاء - التعليقات + التعليقات تأكيد الكلمة السرية تم إنشاؤه معدل diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 4e5064003..6bdf04488 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -48,7 +48,7 @@ Introdueix el nom de la base de dades Accedida Cancel·la - Comentaris + Comentaris Confirma contrasenya Creada Expira diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 8e180628d..2223d5a74 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -52,7 +52,7 @@ Vyberte existující databázi Poslední přístup Storno - Poznámky + Poznámky Potvrďte heslo Vytvořeno Platnost skončí diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index a8607c372..cb04d6b77 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -51,7 +51,7 @@ Vælg en eksisterende database Senest åbnet Annuller - Kommentarer + Kommentarer Bekræft adgangskode Oprettet Udløber diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 150595ce0..8b49fd68b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -54,7 +54,7 @@ Dateinamen der Datenbank eingeben Letzter Zugriff Abbrechen - Kommentare + Kommentare Passwort wiederholen Erstelldatum Ablaufdatum diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index e865639bb..13817ed91 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -49,7 +49,7 @@ Εισαγωγή ονόματος βάσης δεδομένων Προσπελάσθηκε Ακύρωση - Σχόλια + Σχόλια Επιβεβαίωση κωδικού πρόσβασης Δημιουργήθηκε Λήγει diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2571ad8cd..08a6461b0 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -47,7 +47,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Introduzca el nombre del archivo de base de datos Acceso Cancelar - Comentario + Comentario Confirmar contraseña Creación Caducidad diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 9692aa152..679245ce5 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -51,7 +51,7 @@ Datubasearen fitxategiaren izena sartu Akzesoa Utzi - Iruzkinak + Iruzkinak Pasahitza berretsi Sortua Iraungitzen da diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index a9cef0780..150b5644c 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -49,7 +49,7 @@ Anna tietokannan tiedostonimi Käytetty Peruuta - Kommentit + Kommentit Vahvista salasana Luotu Vanhenee diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 51b782e03..fda8b8a79 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -55,7 +55,7 @@ KeePass DX \u00A9 %1$d Kunzisoft n\'offre ABSOLUMENT AUCUNE GARANTIE; il s\'agit d\'un logiciel libre, vous pouvez le redistribuer sous les conditions de la licence GPL v3 ou ultérieure. Dernier accès Annuler - Commentaires + Commentaires Confirmer mot de passe Créé Expire diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 2ebdfd891..20b6f17a0 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -46,7 +46,7 @@ Adja meg az adatbázis fájlnevét Utolsó hozzáférés Mégsem - Megjegyzés + Megjegyzés Jelszó megerősítése Létrehozva Lejárat diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 23a748c5d..cfceb354a 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -52,7 +52,7 @@ Seleziona un database esistente Ultimo accesso Annulla - Commento + Commento Conferma password Creato Scade diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index c6fc37067..d24258849 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -49,7 +49,7 @@ הזן שם קובץ למסד נתונים: ניגש לאחרונה בטל - הערות + הערות אשר סיסמה תאריך יצירה פג תוקף diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 014c47880..dbae37ea0 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -45,7 +45,7 @@ データベースファイル 最終アクセス日 キャンセル - 備考 + 備考 パスワードの確認 作成日 有効期限 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index cbdda6118..b03a99411 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -20,7 +20,7 @@ Duomenų bazė Skaitmenys Atšaukti - Komentarai + Komentarai Sukurta Keista Pasibaigia diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 1ce419451..ea679db62 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -30,7 +30,7 @@ Ievadiet datu bāzes nosaukumu Piekļuve Atcelt - Komentāri + Komentāri Apstipriniet paroli Izveidots Derīguma termiņš beidzas diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 2873f1b68..ef801f86f 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -36,7 +36,7 @@ KeePass DX \u00A9 %1$d Kunzisoft kommer UTEN NOEN FORM FOR GARANTI; Dette er fri programvare, og du er velkommen til å redistribuere det i henhold til vilkårene i GPL versjon 3 eller senere. Brukt Avbryt - Kommentarer + Kommentarer Bekreft passord Opprettet Utløper diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 81a8174ab..b1880e738 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -47,7 +47,7 @@ Kies een bestaande databank Laatst geopend Annuleren - Opmerkingen + Opmerkingen Wachtwoord bevestigen Gecreëerd op Verloopt op diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index 560980553..af56dad01 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -45,7 +45,7 @@ Skriv filnamnet til databasen Brukt Avbryt - Merknader + Merknader Stadfest passordet Laga Går ut diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 5ad65d9df..832a11298 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -43,7 +43,7 @@ along with KeePass DX. If not, see . Wprowadź nazwę pliku bazy danych Dostęp do pliku Anuluj - Komentarz + Komentarz Potwierdź hasło Utworzono Wygasa diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index d9bc3e1f8..f1b6180c0 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -48,7 +48,7 @@ Digite o nome do arquivo de banco de dados Acessado Cancelar - Comentários + Comentários Confirmar senha Criado Expira diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 8adeea46f..140719af6 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -52,7 +52,7 @@ Introduza o nome do ficheiro da base de dados Acedido Cancelar - Comentários + Comentários Confirmar palavra-passe Criado Expira diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 443385c4a..4103aecab 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -50,7 +50,7 @@ Выбрать существующую базу Доступ Отмена - Комментарии + Комментарии Подтверждение Создано Истекает diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 80353edc4..37a49b1a9 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -45,7 +45,7 @@ Vložte názov Databázy Pristupované Zrušiť - Poznámky + Poznámky Potvrdiť heslo Vytvorené Expirácia diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index bfb18afb9..25d61bae8 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -51,7 +51,7 @@ Ange databasnamn Senast använd Avbryt - Kommentarer + Kommentarer Bekräfta lösenord Skapad Upphör att gälla diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3c3a9056f..b674f523f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -45,7 +45,7 @@ Введіть ім’я бази даних Доступ Відміна - Коментар + Коментар Підтвердження паролю Створено Закінчується diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7652c0e77..676932ea6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -45,7 +45,7 @@ 选择一个已有数据库 访问时间 取消 - 备注 + 备注 确认密码 创建 失效时间 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 39bf2f23a..c99c4fc8d 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -45,7 +45,7 @@ 選擇一個已存在之資料庫 訪問時間 取消 - 備註 + 備註 確認密碼 創建 失效時間 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e4d7bc765..7b72c2ee0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,7 +55,7 @@ KeePass DX \u00A9 %1$d Kunzisoft comes with ABSOLUTELY NO WARRANTY; This is libre software, and you are welcome to redistribute it under the conditions of the GPL version 3 or later. Accessed Cancel - Notes + Notes Confirm password Created Expires From 8885192db1d8b8fe7df98d86c1d048e17bf8c99c Mon Sep 17 00:00:00 2001 From: random r Date: Tue, 18 Dec 2018 13:14:44 +0000 Subject: [PATCH 037/289] Translated using Weblate (Italian) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/ --- app/src/main/res/values-it/strings.xml | 284 +++++++++++++------------ 1 file changed, 151 insertions(+), 133 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 23a748c5d..569641f9f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,4 +1,4 @@ - + - - Commenti: - Pagina web: - KeePass DX è un\'implementazione Android del gestore password KeePass. +--> + Commenti + Pagina web + Implementazione Android del gestore password KeePass Accetto Aggiungi voce Aggiungi gruppo Aggiungi stringa - Algoritmo + Algoritmo di cifratura Scadenza app - Tempo prima del blocco del database quando l\'app è inattiva + Inattività prima del blocco dell\'app App Impostazioni app Parentesi - Sfogliare i file richiede che sia installato Gestore File di Open Intents, clicca sotto per farlo. A causa di alcune stranezze nel gestore file, la prima volta la navigazione potrebbe non funzionare correttamente. + Sfoglia i file installando il Gestore File di OpenIntents Annulla Appunti eliminati Errore negli appunti - Alcuni telefoni Android di Samsung hanno un errore nell\'implementazione degli appunti che causa errori nella copia da app. Maggiori dettagli: + Alcuni telefoni Android di Samsung non permettono alle app di usare gli appunti. Eliminazione degli appunti fallita Scadenza appunti - Tempo prima di eliminare gli appunti dopo la copia del nome file o password + Tempo prima di eliminare gli appunti Copia %1$s negli appunti Creazione file chiave database… Database @@ -49,10 +48,10 @@ Usa come database predefinito Numeri KeePass DX \u00A9 %1$d Kunzisoft viene distribuito ASSOLUTAMENTE con NESSUNA GARANZIA; Si tratta di software libero e sei invitato a distribuirlo sotto le condizioni della licenza GPL versione 3 o successiva. - Seleziona un database esistente + Apri un database esistente Ultimo accesso Annulla - Commento + Note Conferma password Creato Scade @@ -69,24 +68,24 @@ Impossibile creare la directory principale. File già esistente. Impossibile aprire il collegamento. - È necessario un nome file. + Inserisci un nome file. Impossibile creare il file: - Database non valido. - Percorso non valido. - È necessario un nome. - Il dispositivo ha esaurito la memoria durante l\'elaborazione del database. - Deve essere selezionato almeno un tipo di generazione password + Lettura del database fallita. + Assicurati che il percorso sia corretto. + Inserisci un nome. + Memoria insufficiente per caricare l\'intero database. + Deve essere selezionato almeno un tipo di generazione password. Le password non corrispondono. - Il \"livello\" deve essere un numero. - \"Livello\" troppo grande. Impostato a 2147483648. + Rendi il \"livello\" un numero. + \"Livello\" troppo alto. Impostato a 2147483648. Ogni stringa deve avere un un nome. - È necessario un titolo. - Inserisci un numero naturale positivo nel campo \"lunghezza\" + Aggiungi un titolo. + Inserisci un numero naturale positivo nel campo \"lunghezza\". Nome campo Valore campo File non trovato. - File non trovato. Prova a riaprire dal tuo fornitore di contenuti. - Sfoglia file + File non trovato. Prova a riaprirlo dal tuo gestore di file. + Gestore file Genera password conferma password password generata @@ -98,7 +97,7 @@ Installa dal Play Store Installa dal F-Droid Password o file chiave non validi. - Algoritmo non valido. + Algoritmo errato. Formato database non riconosciuto. Non esiste alcun file chiave. Il file chiave è vuoto. @@ -107,8 +106,8 @@ Dimensione del testo nell\'elenco del gruppo Caricamento database… Minuscole - Maschera password - Nascondi le password in modo predefinito + Nascondi le password + Maschera le password (***) in modo predefinito Informazioni Modifica chiave principale Impostazioni @@ -125,49 +124,49 @@ Meno Mai Nessun risultato di ricerca - Nessun gestore per questo URL. - Database recenti: + Installa un browser web per aprire questo URL. + Database recenti Non cercare nelle voci di backup - Ometti il gruppo \'Backup\' dai risultati di ricerca (si applica solo ai file .kdb) + Ometti il gruppo \"Backup\" dai risultati di ricerca (si applica solo ai file .kdb) Creazione nuovo database… In corso… Protezione Sola lettura - KeePass DX non ha l\'autorizzazione di scrittura nel percorso del database, quindi verrà aperto in sola lettura. + KeePass DX richiede l\'autorizzazione di scrittura per poter modificare il tuo database. A partire da Android KitKat, alcuni dispositivi non permettono più alle app di scrivere nella scheda SD. Cronologia file recenti - Ricorda i file usati recentemente - Ricorda la posizione dei file chiave + Ricorda i file recenti + Ricorda la posizione dei file chiave dei database Salva il file chiave Elimina Root Livello cifratura - Un livello di cifratura elevato fornisce una protezione maggiore contro attacchi di tipo "forza brutta", ma può veramente rallentare il caricamento e il salvataggio. + Livelli di cifratura aggiuntivi forniscono una maggiore protezione contro attacchi di tipo forza bruta, ma può rallentare il caricamento e il salvataggio. livello Salvataggio database… Spazio Cerca - Ordinamento DB + Database naturale Speciali Titolo/descrizione voce Risultati ricerca Trattino basso Versione database non supportata. Maiuscole - Usa l\'Android Storage Access Framework (SAF) per sfogliare i file (KitKat e successivi) - Storage Access Framework + Usa l\'Android storage access framework (SAF) per sfogliare i file (KitKat e successivi) + Storage access framework Attenzione - La tua password potrebbe contenere lettere non supportate dal set di caratteri Latin-1 usato dai file .kdb . Dato che sono convertite tutte nello stesso carattere, si consiglia di modificare la password per renderla più sicura. - Attualmente la scheda SD è in sola lettura. Non potrai salvare le modifiche nel database. - Attualmente la scheda SD non è montata nel dispositivo. Non potrai caricare o creare un nuovo database. + Evita password con caratteri al di fuori del set Latin-1 nei file .kdb, dato che sono convertiti tutti nella stessa lettera. + Permetti l\'accesso in scrittura alla scheda SD per salvare le modifiche al database. + Monta la scheda SD per creare o caricare un database. Versione %1$s - Le impronte sono supportate ma non configurate nel dispositivo - In attesa di impronte + La scansione di impronte è supportata ma non impostata. + Scansione impronte Password criptata salvata - Problema impronta non valida. Ripristina la tua password. + Lettura dell\'impronta fallita. Ripristina la tua password. Problema impronta: %1$s Usa l\'impronta per salvare questa password - Nessuna password salvata per questo database + Questo database non ha ancora alcuna password. Inserisci una password e/o file chiave per sbloccare il database. \n @@ -192,23 +191,23 @@ Non mostrare di nuovo Consenti Copia di %1$s - Crittografia + Cifratura Funzione di derivazione chiave ASCII esteso - Scorri ora per pulire gli appunti - È richiesto un file chiave. - Il servizio di auto-riempimento non può essere attivato. - Impossibile spostare un gruppo in se stesso. + Scorri per pulire gli appunti ora + Seleziona un file chiave. + Attivazione del servizio di auto-completamento fallita. + Non puoi spostare un gruppo in se stesso. Riempimento campi Copia Sposta Incolla Annulla - Rimuovi l\'impronta digitale - Solo lettura - Lettura e scrittura - Algoritmo per cifrare l\'intero database. (Password, nome utente, note e tutti i dati del database sono criptati con l\'algoritmo selezionato) - Per generare la chiave per l\'algoritmo di cifratura, la chiave master compressa (SHA-256) viene trasformata usando una funzione di derivazione della chiave (con un sale casuale). + Elimina l\'impronta digitale salvata + Sola lettura + Modificabile + Algoritmo di cifratura del database usato per tutti i dati. + Per generare la chiave per l\'algoritmo di cifratura, la chiave principale viene trasformata usando una funzione di derivazione della chiave (con un sale casuale). Utilizzo di memoria Quantità di memoria (in byte binari) da usare dalla funzione di derivazione della chiave. Parallelismo @@ -219,137 +218,137 @@ Cestino in fondo Titolo Nome utente - Data di creazione - Ultima modifica - Ultimo accesso - Vuoi veramente usare una stringa vuota come password? + Creazione + Modifica + Accesso + Vuoi veramente che non ci sia una password di sblocco\? Sei sicuro di non volere usare una chiave di cifratura? Impronta non riconosciuta Cronologia Aspetto Generale Autocompletamento - Servizio autocompletamento di KeePass DX + Autocompletamento di KeePass DX Accedi con KeePass DX Imposta servizio predefinito di autocompletamento - Attiva il servizio per completare facilmente i moduli da altre app - Dimensione password - Imposta la dimensione predefinita della password generata + Attiva l\'autocompletamento per compilare velocemente i moduli in altre app + Dimensione password generata + Imposta la dimensione predefinita delle password generate Caratteri password - Imposta i caratteri predefiniti di generazione password + Imposta i caratteri permessi di generazione password Notifiche appunti Attiva le notifiche appunti per copiare gli elementi - Se il tuo dispositivo non riesce ad eliminare gli appunti automaticamente, elimina manualmente dagli appunti l\'elemento copiato. + Se l\'eliminazione automatica degli appunti fallisce, cancellali manualmente. Blocca Blocco schermo Blocca il database quando lo schermo è spento - Come configurare l\'impronta per sbloccare velocemente? - Imposta la tua impronta per il dispositivo in + Come impostare la scansione di impronte per sbloccare velocemente\? + Salva la tua impronta per il dispositivo in \"Impostazioni\" → \"Sicurezza\" → \"Impronta\" - Digita la tua password in Keepass DX - Scansiona la tua impronta per salvare la tua password master in modo sicuro - Scansiona la tua impronta quando la casella password non è selezionata per aprire il database + Digita la password di blocco database + Scansiona la tua impronta per salvare la password di blocco database in modo sicuro. + Scansiona la tua impronta per aprire il database quando la password è disattivata. Utilizzo Impronta Scansione di impronte - Attiva apertura database con impronta + Consente la scansione di impronte per aprire il database Elimina chiavi di cifratura Elimina tutte le chiavi di cifratura relative al riconoscimento dell\'impronta Sei sicuro di volere eliminare tutte le chiavi relative alle impronte? Impossibile avviare questa funzione. La tua versione di Android %1$s non è la minima %2$s richiesta. - L\'hardware non è stato rilevato. + L\'hardware relativo non è stato trovato. Nome file Percorso Assegna una chiave master - Crea un file KeePass + Crea un nuovo database Bytes Percorso file Visualizza il percorso file completo Usa il cestino - Sposta un gruppo o elemento nel cestino prima di eliminare - KeePass DX richiede l\'autorizzazione di archiviazione per scrivere un database - KeePass DX richiede l\'autorizzazione di archiviazione per leggere un URI non gestito da un fornitore di contenuti - Autorizzazione di archiviazione negata - Impossibile eseguire l\'azione senza l\'autorizzazione di archiviazione + Sposta gruppi ed elementi nel cestino prima di eliminare + KeePass DX richiede l\'autorizzazione di archiviazione per scrivere in un database. + KeePass DX richiede l\'autorizzazione di archiviazione per leggere un URI non gestito da un fornitore di contenuti. + Autorizzazione di archiviazione non ottenuta. + Impossibile eseguire l\'azione senza l\'autorizzazione di archiviazione. Carattere campi Cambia il carattere usato nei campi per una migliore visibilità - Apri automaticamente file selezionato - Apri un file dalla finestra di selezione automaticamente dopo una selezione fatta nel gestore file - Copia della password + Apri i file selezionandoli + Apri automaticamente i file selezionandoli nel gestore di file + Fiducia appunti Permetti la copia della password e dei campi protetti negli appunti ATTENZIONE: gli appunti sono condivisi da tutte le app. Se vengono copiati dati sensibili, altri software possono recuperarli. - ATTENZIONE: disattivando questa funzione potresti non riuscire ad aprire o salvare database - Link del file KDBX da aprire + ATTENZIONE: disattivare questa funzione può impedire l\'apertura o salvataggio dei database. + Link del file database da aprire Nome database Descrizione database Versione database - Aspetto del testo - Aspetto dell\'app + Testo + App Altro Tastiera Magitastiera - Attiva una tastiera personale che popola le tue password e i campi di identità facilmente + Attiva una tastiera personale che popola le tue password e i campi di identità Impostazioni Magitastiera - Come configurare la tastiera per una compilazione di campi sicura? - Attiva la Magitastiera nelle impostazioni del dispositivo. + Imposta la tastiera per un\'autocompletamento sicuro dei moduli. + Attiva la \"Magitastiera\" nelle impostazioni del dispositivo. \"Impostazioni\" → \"Lingue e immissione\" → \"Tastiera attuale\" e scegline una. o (\"Impostazioni\" → \"Lingue e immissione\" → \"Tastiera virtuale\" e scegline una.) Scegli la Magitastiera quando devi compilare un modulo. - Puoi passare facilmente dalla tastiera principale alla Magitastiera con il pulsante lingua della tastiera, una pressione lunga della barra spaziatrice, o, se non disponibile, con: + Cambia tastiera premendo a lungo la barra spaziatrice, oppure, se non disponibile, con: Blocca il database. - Torna alla tua tastiera principale. + Usa di nuovo la tastiera predefinita. Permetti password mancante - Attiva il pulsante di apertura se non è selezionata una password - Solo lettura - Apri i database in sola lettura in modo predefinito + Attiva il pulsante \"Apri\" se non è selezionata l\'identificazione della password + Sola lettura + Apri il database in sola lettura in modo predefinito Schermate educative Evidenzia gli elementi per imparare come funziona l\'app Ripristina schermate educative - Ripristina la visualizzazione di elementi educativi + Mostra di nuovo gli elementi educativi Schermate educative ripristinate Crea il tuo file database - Non conosci ancora KeePass DX, crea il tuo primo file di gestione password. + Crea il tuo primo file di gestione password. Apri un database esistente - Hai già usato un gestore KeePass. Apri il tuo file KDBX dal tuo gestore di file. + Apri il file database precedente dal tuo gestore di file per continuare ad usarlo. Un link al percorso del tuo file è sufficiente Puoi anche aprire il tuo database con un link fisico (con file:// e content:// ad esempio). - Aggiungi nuovi elementi al database - Aggiungi elementi per gestire le tue identità digitali. -\n -\nAggiungi gruppi (equivalente delle cartelle) per organizzare elementi e database. - Cerca facilmente gli elementi - Cerca elementi per titolo, nome utente o altri campi per trovare facilmente le password. - Sblocca il database con la tua impronta - Crea un collegamento tra la tua password e la tua impronta per sbloccare facilmente il database. - Modifica l\'elemento - Modifica l\'elemento con campi personali, puoi aggiungere riferimenti per raggruppare i dati tra campi di voci diverse. - Crea una password robusta - Genera una password robusta da associare all\'elemento, definiscila a seconda dei criteri del modulo e non dimenticare una password sicura che puoi ricordare. - Aggiungi campi personali - Vuoi registrare un campo di base non fornito, inseriscine uno nuovo che puoi anche proteggere visivamente. - Sblocca il tuo database - Attiva solo lettura - Cambia la modalità di apertura per la sessione. -\n -\nIn modalità di sola lettura, eviti modifiche accidentali al database. -\n -\nIn scrittura, puoi aggiungere, eliminare, o modificare tutti gli elementi che vuoi. - Copia un campo - Copia un campo facilmente per incollarlo dove vuoi + Aggiungi elementi al database + Gli elementi aiutano a gestire le tue identità digitali. \n -\nPuoi usare diversi metodi di inserimento moduli. Usa quello che preferisci. +\nI gruppi (~ cartelle) organizzano gli elementi nel database. + Cerca tra gli elementi + Digita titolo, nome utente o il contenuto di altri campi per trovare le tue password. + Sblocco del database con impronta + Collega la password alla tua impronta per sbloccare velocemente il database. + Modifica l\'elemento + Modifica l\'elemento con campi personali. I dati possono avere riferimenti ad altri campi. + Crea una password robusta per l\'elemento. + Genera una password robusta da associare all\'elemento, definiscila a seconda dei criteri del modulo e non dimenticare di tenerla al sicuro. + Aggiungi campi personali + Registra un campo base non fornito, inserendone uno nuovo che puoi anche proteggere. + Sblocca il tuo database + Proteggi da scrittura il database + Cambia modalità di apertura per la sessione. +\n +\n\"Sola lettura\" impedisce modifiche accidentali al database. +\n +\n\"Modificabile\" permette di aggiungere, eliminare o modificare tutti gli elementi. + Copia un campo + I campi copiati possono essere incollati ovunque. +\n +\nUsa il metodo di inserimento che preferisci. Blocca il database Blocca velocemente il database, puoi impostare l\'app per bloccarlo dopo un certo periodo e quando lo schermo si spegne. - Ordina elementi - Ordina gli elementi e i gruppi secondo parametri specifici. + Ordine elementi + Scegli l\'ordine di elementi e gruppi. Partecipa - Partecipa per aiutare a migliorare la stabilità, la sicurezza e aggiungere nuove funzioni. + Aiuta a migliorare la stabilità, la sicurezza e ad aggiungere nuove funzioni. - Diversamente da molte app di gestione password, questa è senza pubblicità, sofware libero (copyleft) e non raccoglie dati personali nei suoi server, anche nella versione gratuita. + Diversamente da molte app di gestione password, questa è senza pubblicità, sofware libero (copyleft) e non raccoglie dati personali nei suoi server, non importa quale versione usi. Acquistando la versione pro, avrai accesso a questa funzione visiva e soprattutto aiuterai nella realizzazione di progetti della comunità. Questa funzione visiva è disponibile grazie alla tua generosità. Per mantenere la nostra libertà ed essere sempre attivi, contiamo sul tuo contributo. @@ -360,7 +359,7 @@ incoraggi gli sviluppatori a creare nuove funzioni e a correggere errori secondo le tue osservazioni. Grazie mille per il tuo contributo. Stiamo lavorando sodo per rilasciare questa funzione a breve. - Non dimenticare di tenere aggiornata la tua app. + Non dimenticare di tenere aggiornata l\'app installando nuove versioni. Scarica Contribuisci @@ -372,17 +371,36 @@ AES KDF Argon2 - Seleziona un tema - Cambia il tema dell\'app modificandone i colori - Seleziona un pacchetto icone - Cambia il pacchetto di icone dell\'app + Tema app + Tema usato nell\'app + Pacchetto icone + Pacchetto di icone usato nell\'app Seleziona un elemento con la chiave. - Riempi i campi usando i contenuti dell\'elemento. + Riempi i campi usando gli elementi giusti. Modifica elemento - Il database non può essere caricato - Impossibile caricare la chiave, prova a diminuire la memoria usata dal KDF. + Caricamento del database fallito. + Caricamento della chiave fallito. Prova a diminuire l\' \"Utilizzo memoria\" del KDF. Mostra nomi utente Mostra i nomi utente negli elenchi Appunti - + Build %1$s + Magitastiera + Magitastiera (KeePass DX) + Impostazioni Magitastiera + Inserimento + Scadenza + Tempo per eliminare l\'inserimento da tastiera + Informazioni di notifica + Mostra una notifica quando un inserimento è disponibile + Inserimento + %1$s disponibile nella Magitastiera + %1$s + Pulisci alla chiusura + Pulisci l\'inserimento da tastiera alla chiusura della notifica + Aspetto + Tema tastiera + Tasti + Vibra alla pressione + Suono alla pressione + \ No newline at end of file From 4fcb2de2bb38efe225ff82f929bb152cbf7f1526 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 19 Dec 2018 17:20:37 +0100 Subject: [PATCH 038/289] Rename lock or reset timeout method --- .../java/com/kunzisoft/keepass/activities/GroupActivity.java | 2 +- .../com/kunzisoft/keepass/activities/lock/LockingActivity.kt | 4 ++-- .../main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 7a36a9b71..7b7c9fd13 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -345,7 +345,7 @@ public class GroupActivity extends LockingActivity private void openGroup(PwGroup group, boolean isASearch) { // Check TimeoutHelper - TimeoutHelper.INSTANCE.resetTime(this, () -> { + TimeoutHelper.INSTANCE.lockOrResetTimeout(this, () -> { // Open a group in a new fragment ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, getReadOnly(), isASearch); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index 62249797b..b582539c2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -134,14 +134,14 @@ abstract class LockingActivity : StylishActivity() { views.forEach { it.setOnFocusChangeListener { _, hasFocus -> if (hasFocus) { - TimeoutHelper.resetTime(this) + TimeoutHelper.lockOrResetTimeout(this) } } } } override fun onBackPressed() { - TimeoutHelper.resetTime(this) { + TimeoutHelper.lockOrResetTimeout(this) { super.onBackPressed() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index bfd9a8044..19ea18219 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -136,7 +136,7 @@ object TimeoutHelper { return true } - fun resetTime(activity: Activity, action: (() -> Unit)? = null) { + fun lockOrResetTimeout(activity: Activity, action: (() -> Unit)? = null) { if (checkTime(activity)) { recordTime(activity) action?.invoke() From a535f123ffe80571bff012f4279f951410b2c656 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 19 Dec 2018 19:04:44 +0100 Subject: [PATCH 039/289] Fix settings timeout --- .../keepass/activities/EntryActivity.java | 4 +- .../keepass/activities/EntryEditActivity.java | 4 +- .../keepass/activities/GroupActivity.java | 4 +- .../activities/lock/LockingActivity.kt | 49 ++++--- .../fileselect/FileSelectActivity.java | 4 +- .../keepass/password/PasswordActivity.java | 4 +- .../keepass/settings/SettingsActivity.java | 126 ----------------- .../keepass/settings/SettingsActivity.kt | 133 ++++++++++++++++++ .../com/kunzisoft/keepass/utils/MenuUtil.java | 86 ----------- .../com/kunzisoft/keepass/utils/MenuUtil.kt | 82 +++++++++++ 10 files changed, 256 insertions(+), 240 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index f6374de5d..a4a30f5c9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -424,7 +424,7 @@ public class EntryActivity extends LockingHideActivity { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); - MenuUtil.contributionMenuInflater(inflater, menu); + MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); inflater.inflate(R.menu.entry, menu); inflater.inflate(R.menu.database_lock, menu); @@ -468,7 +468,7 @@ public class EntryActivity extends LockingHideActivity { public boolean onOptionsItemSelected(MenuItem item) { switch ( item.getItemId() ) { case R.id.menu_contribute: - return MenuUtil.onContributionItemSelected(this); + return MenuUtil.INSTANCE.onContributionItemSelected(this); case R.id.menu_toggle_pass: mShowPassword = !mShowPassword; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index b40c5f2e9..acdb9268b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -471,7 +471,7 @@ public class EntryEditActivity extends LockingHideActivity super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); - MenuUtil.contributionMenuInflater(inflater, menu); + MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); return true; } @@ -479,7 +479,7 @@ public class EntryEditActivity extends LockingHideActivity public boolean onOptionsItemSelected(MenuItem item) { switch ( item.getItemId() ) { case R.id.menu_contribute: - return MenuUtil.onContributionItemSelected(this); + return MenuUtil.INSTANCE.onContributionItemSelected(this); case android.R.id.home: finish(); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 7b7c9fd13..f8418a1c7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -892,7 +892,7 @@ public class GroupActivity extends LockingActivity }); } - MenuUtil.contributionMenuInflater(inflater, menu); + MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); inflater.inflate(R.menu.default_menu, menu); super.onCreateOptionsMenu(menu); @@ -956,7 +956,7 @@ public class GroupActivity extends LockingActivity return true; default: // Check the time lock before launching settings - MenuUtil.onDefaultMenuOptionsItemSelected(this, item, getReadOnly(), true); + MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item, getReadOnly(), true); return super.onOptionsItemSelected(item); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index b582539c2..5bde5b4b1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -43,6 +43,8 @@ abstract class LockingActivity : StylishActivity() { const val RESULT_EXIT_LOCK = 1450 } + protected var timeoutEnable: Boolean = true + private var lockReceiver: LockReceiver? = null private var exitLock: Boolean = false @@ -51,14 +53,15 @@ abstract class LockingActivity : StylishActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this)) { - lockReceiver = LockReceiver() - val intentFilter = IntentFilter() - intentFilter.addAction(Intent.ACTION_SCREEN_OFF) - intentFilter.addAction(LOCK_ACTION) - registerReceiver(lockReceiver, IntentFilter(intentFilter)) - } else - lockReceiver = null + if (timeoutEnable) { + if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this)) { + lockReceiver = LockReceiver() + val intentFilter = IntentFilter() + intentFilter.addAction(Intent.ACTION_SCREEN_OFF) + intentFilter.addAction(LOCK_ACTION) + registerReceiver(lockReceiver, IntentFilter(intentFilter)) + } + } exitLock = false @@ -76,13 +79,16 @@ abstract class LockingActivity : StylishActivity() { override fun onResume() { super.onResume() - // After the first creation - // or If simply swipe with another application - // If the time is out -> close the Activity - TimeoutHelper.checkTime(this) - // If onCreate already record time - if (!exitLock) - TimeoutHelper.recordTime(this) + + if (timeoutEnable) { + // After the first creation + // or If simply swipe with another application + // If the time is out -> close the Activity + TimeoutHelper.checkTime(this) + // If onCreate already record time + if (!exitLock) + TimeoutHelper.recordTime(this) + } } override fun onSaveInstanceState(outState: Bundle) { @@ -92,8 +98,11 @@ abstract class LockingActivity : StylishActivity() { override fun onPause() { super.onPause() - // If the time is out during our navigation in activity -> close the Activity - TimeoutHelper.checkTime(this) + + if (timeoutEnable) { + // If the time is out during our navigation in activity -> close the Activity + TimeoutHelper.checkTime(this) + } } override fun onDestroy() { @@ -141,7 +150,11 @@ abstract class LockingActivity : StylishActivity() { } override fun onBackPressed() { - TimeoutHelper.lockOrResetTimeout(this) { + if (timeoutEnable) { + TimeoutHelper.lockOrResetTimeout(this) { + super.onBackPressed() + } + } else { super.onBackPressed() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java index 740b47a5e..966ea388b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java @@ -697,13 +697,13 @@ public class FileSelectActivity extends StylishActivity implements @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - MenuUtil.defaultMenuInflater(getMenuInflater(), menu); + MenuUtil.INSTANCE.defaultMenuInflater(getMenuInflater(), menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) + return MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index a570b6442..3be726f77 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -1027,7 +1027,7 @@ public class PasswordActivity extends StylishActivity inflater.inflate(R.menu.open_file, menu); changeOpenFileReadIcon(menu.findItem(R.id.menu_open_file_read_mode_key)); - MenuUtil.defaultMenuInflater(inflater, menu); + MenuUtil.INSTANCE.defaultMenuInflater(inflater, menu); // Fingerprint menu if (!fingerprintMustBeConfigured @@ -1069,7 +1069,7 @@ public class PasswordActivity extends StylishActivity } break; default: - return MenuUtil.onDefaultMenuOptionsItemSelected(this, item); + return MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item); } return super.onOptionsItemSelected(item); diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java deleted file mode 100644 index b08be9451..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.settings; - -import android.app.Activity; -import android.app.backup.BackupManager; -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v7.widget.Toolbar; -import android.view.MenuItem; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.ReadOnlyHelper; -import com.kunzisoft.keepass.activities.lock.LockingActivity; -import com.kunzisoft.keepass.timeout.TimeoutHelper; - - -public class SettingsActivity extends LockingActivity implements MainPreferenceFragment.Callback { - - private static final String TAG_NESTED = "TAG_NESTED"; - - private BackupManager backupManager; - - private Toolbar toolbar; - - public static void launch(Activity activity, boolean readOnly) { - Intent intent = new Intent(activity, SettingsActivity.class); - ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly); - activity.startActivity(intent); - } - - public static void launch(Activity activity, boolean readOnly, boolean checkLock) { - // To avoid flickering when launch settings in a LockingActivity - if (!checkLock) - launch(activity, readOnly); - else if (TimeoutHelper.INSTANCE.checkTime(activity)) { - launch(activity, readOnly); - } - } - - /** - * Retrieve the main fragment to show in first - * @return The main fragment - */ - protected Fragment retrieveMainFragment() { - return new MainPreferenceFragment(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_toolbar); - toolbar = findViewById(R.id.toolbar); - toolbar.setTitle(R.string.settings); - setSupportActionBar(toolbar); - assert getSupportActionBar() != null; - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - if (savedInstanceState == null) { - getSupportFragmentManager().beginTransaction() - .add(R.id.fragment_container, retrieveMainFragment()) - .commit(); - } - - backupManager = new BackupManager(this); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch ( item.getItemId() ) { - case android.R.id.home: - onBackPressed(); - break; - } - - return super.onOptionsItemSelected(item); - } - - @Override - protected void onStop() { - backupManager.dataChanged(); - super.onStop(); - } - - @Override - public void onBackPressed() { - // this if statement is necessary to navigate through nested and main fragments - if (getSupportFragmentManager().getBackStackEntryCount() == 0) { - super.onBackPressed(); - } else { - getSupportFragmentManager().popBackStack(); - } - toolbar.setTitle(R.string.settings); - } - - @Override - public void onNestedPreferenceSelected(NestedSettingsFragment.Screen key) { - getSupportFragmentManager().beginTransaction() - .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, - R.anim.slide_in_left, R.anim.slide_out_right) - .replace(R.id.fragment_container, NestedSettingsFragment.newInstance(key, getReadOnly()), TAG_NESTED) - .addToBackStack(TAG_NESTED) - .commit(); - - toolbar.setTitle(NestedSettingsFragment.retrieveTitle(getResources(), key)); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt new file mode 100644 index 000000000..40d6148cc --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.settings + +import android.app.Activity +import android.app.backup.BackupManager +import android.content.Intent +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v7.widget.Toolbar +import android.view.MenuItem + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.ReadOnlyHelper +import com.kunzisoft.keepass.activities.lock.LockingActivity +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.timeout.TimeoutHelper + + +open class SettingsActivity : LockingActivity(), MainPreferenceFragment.Callback { + + private var backupManager: BackupManager? = null + + private var toolbar: Toolbar? = null + + companion object { + + private const val TAG_NESTED = "TAG_NESTED" + + fun launch(activity: Activity, readOnly: Boolean) { + val intent = Intent(activity, SettingsActivity::class.java) + ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly) + activity.startActivity(intent) + } + + fun launch(activity: Activity, readOnly: Boolean, checkLock: Boolean) { + // To avoid flickering when launch settings in a LockingActivity + if (!checkLock) + launch(activity, readOnly) + else if (TimeoutHelper.checkTime(activity)) { + launch(activity, readOnly) + } + } + } + + /** + * Retrieve the main fragment to show in first + * @return The main fragment + */ + protected open fun retrieveMainFragment(): Fragment { + return MainPreferenceFragment() + } + + override fun onCreate(savedInstanceState: Bundle?) { + timeoutEnable = App.getDB().loaded + + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_toolbar) + toolbar = findViewById(R.id.toolbar) + toolbar?.setTitle(R.string.settings) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction() + .add(R.id.fragment_container, retrieveMainFragment()) + .commit() + } + + backupManager = BackupManager(this) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> onBackPressed() + } + + return super.onOptionsItemSelected(item) + } + + override fun onStop() { + backupManager?.dataChanged() + super.onStop() + } + + override fun onBackPressed() { + // this if statement is necessary to navigate through nested and main fragments + if (supportFragmentManager.backStackEntryCount == 0) { + super.onBackPressed() + } else { + supportFragmentManager.popBackStack() + } + toolbar?.setTitle(R.string.settings) + } + + private fun replaceFragment(key: NestedSettingsFragment.Screen) { + supportFragmentManager.beginTransaction() + .setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, + R.anim.slide_in_left, R.anim.slide_out_right) + .replace(R.id.fragment_container, NestedSettingsFragment.newInstance(key, readOnly), TAG_NESTED) + .addToBackStack(TAG_NESTED) + .commit() + + toolbar?.title = NestedSettingsFragment.retrieveTitle(resources, key) + } + + override fun onNestedPreferenceSelected(key: NestedSettingsFragment.Screen) { + if (timeoutEnable) + TimeoutHelper.lockOrResetTimeout(this) { + replaceFragment(key) + } + else + replaceFragment(key) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.java b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.java deleted file mode 100644 index a5ad75db8..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.utils; - -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.Toast; - -import com.kunzisoft.keepass.BuildConfig; -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.AboutActivity; -import com.kunzisoft.keepass.settings.SettingsActivity; -import com.kunzisoft.keepass.stylish.StylishActivity; - -import static com.kunzisoft.keepass.activities.ReadOnlyHelper.READ_ONLY_DEFAULT; - - -public class MenuUtil { - - public static void contributionMenuInflater(MenuInflater inflater, Menu menu) { - if(!(BuildConfig.FULL_VERSION && BuildConfig.CLOSED_STORE)) - inflater.inflate(R.menu.contribution, menu); - } - - public static void defaultMenuInflater(MenuInflater inflater, Menu menu) { - contributionMenuInflater(inflater, menu); - inflater.inflate(R.menu.default_menu, menu); - } - - public static boolean onContributionItemSelected(StylishActivity activity) { - try { - Util.gotoUrl(activity, R.string.contribution_url); - } catch (ActivityNotFoundException e) { - Toast.makeText(activity, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show(); - return false; - } - return true; - } - - public static boolean onDefaultMenuOptionsItemSelected(StylishActivity activity, MenuItem item) { - return onDefaultMenuOptionsItemSelected(activity, item, READ_ONLY_DEFAULT, false); - } - - /* - * @param checkLock Check the time lock before launch settings in LockingActivity - */ - public static boolean onDefaultMenuOptionsItemSelected(StylishActivity activity, MenuItem item, boolean readOnly, boolean checkLock) { - switch (item.getItemId()) { - case R.id.menu_contribute: - return onContributionItemSelected(activity); - - case R.id.menu_app_settings: - // To avoid flickering when launch settings in a LockingActivity - SettingsActivity.launch(activity, readOnly, checkLock); - return true; - - case R.id.menu_about: - Intent intent = new Intent(activity, AboutActivity.class); - activity.startActivity(intent); - return true; - - default: - return true; - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt new file mode 100644 index 000000000..e7378264c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2018 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.utils + +import android.content.ActivityNotFoundException +import android.content.Intent +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.widget.Toast +import com.kunzisoft.keepass.BuildConfig +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.AboutActivity +import com.kunzisoft.keepass.activities.ReadOnlyHelper.READ_ONLY_DEFAULT +import com.kunzisoft.keepass.settings.SettingsActivity +import com.kunzisoft.keepass.stylish.StylishActivity + + +object MenuUtil { + + fun contributionMenuInflater(inflater: MenuInflater, menu: Menu) { + if (!(BuildConfig.FULL_VERSION && BuildConfig.CLOSED_STORE)) + inflater.inflate(R.menu.contribution, menu) + } + + fun defaultMenuInflater(inflater: MenuInflater, menu: Menu) { + contributionMenuInflater(inflater, menu) + inflater.inflate(R.menu.default_menu, menu) + } + + fun onContributionItemSelected(activity: StylishActivity): Boolean { + try { + Util.gotoUrl(activity, R.string.contribution_url) + } catch (e: ActivityNotFoundException) { + Toast.makeText(activity, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show() + return false + } + + return true + } + + /* + * @param checkLock Check the time lock before launch settings in LockingActivity + */ + @JvmOverloads + fun onDefaultMenuOptionsItemSelected(activity: StylishActivity, item: MenuItem, readOnly: Boolean = READ_ONLY_DEFAULT, checkLock: Boolean = false): Boolean { + when (item.itemId) { + R.id.menu_contribute -> return onContributionItemSelected(activity) + + R.id.menu_app_settings -> { + // To avoid flickering when launch settings in a LockingActivity + SettingsActivity.launch(activity, readOnly, checkLock) + return true + } + + R.id.menu_about -> { + val intent = Intent(activity, AboutActivity::class.java) + activity.startActivity(intent) + return true + } + + else -> return true + } + } +} From 0ad8826b51abd7eee1c9d753b3bacd9ba7875882 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 19 Dec 2018 22:27:06 +0100 Subject: [PATCH 040/289] New lock implementation --- app/src/main/AndroidManifest.xml | 4 +- .../keepass/activities/EntryActivity.java | 7 +- .../keepass/activities/EntryEditActivity.java | 4 -- .../keepass/activities/GroupActivity.java | 9 +-- .../activities/lock/LockingActivity.kt | 19 +++--- .../java/com/kunzisoft/keepass/app/App.java | 25 ------- .../autofill/AutoFillAuthActivity.java | 68 ------------------- .../keepass/autofill/AutoFillAuthActivity.kt | 67 ++++++++++++++++++ .../keepass/autofill/KeeAutofillService.java | 2 +- .../kunzisoft/keepass/database/Database.java | 10 +-- .../action/CreateDatabaseRunnable.java | 3 +- .../action/node/DeleteGroupRunnable.java | 2 - .../magikeyboard/EntryRetrieverActivity.java | 59 ---------------- .../KeyboardEntryNotificationService.java | 2 +- .../keepass/magikeyboard/MagikIME.java | 3 +- .../keepass/password/PasswordActivity.java | 14 +--- .../selection/EntrySelectionAuthActivity.java | 46 ------------- .../selection/EntrySelectionHelper.java | 2 +- .../KeyboardEntryRetrieverActivity.kt | 60 ++++++++++++++++ .../settings/MainPreferenceFragment.java | 2 +- .../settings/NestedSettingsFragment.java | 2 +- .../keepass/timeout/TimeoutHelper.kt | 27 ++++---- 22 files changed, 164 insertions(+), 273 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/magikeyboard/EntryRetrieverActivity.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionAuthActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b88da678d..d9f486a01 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -128,10 +128,8 @@ - - . - * - */ -package com.kunzisoft.keepass.autofill; - -import android.app.PendingIntent; -import android.app.assist.AssistStructure; -import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresApi; -import android.support.v7.app.AppCompatActivity; - -import com.kunzisoft.keepass.fileselect.FileSelectActivity; - -@RequiresApi(api = Build.VERSION_CODES.O) -public class AutoFillAuthActivity extends AppCompatActivity { - - private AutofillHelper autofillHelper; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - autofillHelper = new AutofillHelper(); - startFileSelectActivity(); - super.onCreate(savedInstanceState); - } - - public static IntentSender getAuthIntentSenderForResponse(Context context) { - final Intent intent = new Intent(context, AutoFillAuthActivity.class); - return PendingIntent.getActivity(context, 0, - intent, PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); - } - - protected void startFileSelectActivity() { - // Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE) - AssistStructure assistStructure = autofillHelper.retrieveAssistStructure(getIntent()); - if (assistStructure != null) { - FileSelectActivity.launchForAutofillResult(this, assistStructure); - } else { - setResult(RESULT_CANCELED); - finish(); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt new file mode 100644 index 000000000..fed3c49c6 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2017 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.autofill + +import android.app.Activity +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.IntentSender +import android.os.Build +import android.os.Bundle +import android.support.annotation.RequiresApi +import android.support.v7.app.AppCompatActivity +import com.kunzisoft.keepass.activities.GroupActivity +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.fileselect.FileSelectActivity + +@RequiresApi(api = Build.VERSION_CODES.O) +class AutoFillAuthActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + // Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE) + val assistStructure = AutofillHelper().retrieveAssistStructure(intent) + if (assistStructure != null) { + if (App.getDB().loaded) + GroupActivity.launchForAutofillResult(this, assistStructure, true) + else { + FileSelectActivity.launchForAutofillResult(this, assistStructure) + } + } else { + setResult(Activity.RESULT_CANCELED) + finish() + } + + super.onCreate(savedInstanceState) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) + } + + companion object { + + fun getAuthIntentSenderForResponse(context: Context): IntentSender { + val intent = Intent(context, AutoFillAuthActivity::class.java) + return PendingIntent.getActivity(context, 0, + intent, PendingIntent.FLAG_CANCEL_CURRENT).intentSender + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java index 3929bec47..932509972 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java @@ -62,7 +62,7 @@ public class KeeAutofillService extends AutofillService { if (!Arrays.asList(autofillIds).isEmpty()) { // If the entire Autofill Response is authenticated, AuthActivity is used // to generate Response. - IntentSender sender = AutoFillAuthActivity.getAuthIntentSenderForResponse(this); + IntentSender sender = AutoFillAuthActivity.Companion.getAuthIntentSenderForResponse(this); RemoteViews presentation = new RemoteViews(getPackageName(), R.layout.autofill_service_unlock); responseBuilder.setAuthentication(autofillIds, sender, presentation); callback.onSuccess(responseBuilder.build()); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/Database.java index ce5c4ce50..41ec09810 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/Database.java @@ -68,7 +68,7 @@ public class Database { private IconDrawableFactory drawFactory = new IconDrawableFactory(); - private boolean loaded = false; + public boolean loaded = false; public PwDatabase getPwDatabase() { return pwDatabase; @@ -98,14 +98,6 @@ public class Database { return drawFactory; } - public boolean getLoaded() { - return loaded; - } - - public void setLoaded() { - loaded = true; - } - public void loadData(Context ctx, Uri uri, String password, Uri keyfile, ProgressTaskUpdater status) throws IOException, FileNotFoundException, InvalidDBException { loadData(ctx, uri, password, keyfile, status, !Importer.DEBUG); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java index 5d10a3e34..acd6cd5f7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java @@ -52,8 +52,7 @@ public class CreateDatabaseRunnable extends RunnableOnFinish { // Set Database state db.setPwDatabase(pm); db.setUri(UriUtil.parseDefaultFile(mFilename)); - db.setLoaded(); - App.clearShutdown(); + db.loaded = true; // Commit changes SaveDatabaseRunnable save = new SaveDatabaseRunnable(mContext, db, mFinish, mDontSave); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java index b41a6b414..92c13b6b7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java @@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.action.node; import android.content.Context; -import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.database.Database; import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwGroup; @@ -87,7 +86,6 @@ public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { } else { // Let's not bother recovering from a failure to save a deleted tree. It is too much work. - App.setShutdown(); // TODO TEST pm.undoDeleteGroup(mGroup, mParent); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/EntryRetrieverActivity.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/EntryRetrieverActivity.java deleted file mode 100644 index 0b4c9fa1b..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/EntryRetrieverActivity.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.kunzisoft.keepass.magikeyboard; - -import android.app.Activity; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.model.Entry; - -public class EntryRetrieverActivity extends AppCompatActivity { - - public static final String TAG = EntryRetrieverActivity.class.getName(); - - public static final int ENTRY_REQUEST_CODE = 271; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent; - try { - intent = new Intent(this, - Class.forName("com.kunzisoft.keepass.selection.EntrySelectionAuthActivity")); - startActivityForResult(intent, ENTRY_REQUEST_CODE); - } catch (ClassNotFoundException e) { - Log.e(TAG, "Unable to load the entry retriever", e); - finish(); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - Log.i(TAG, "Retrieve the entry selected"); - if (requestCode == ENTRY_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - Entry entry = data.getParcelableExtra("com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE"); - MagikIME.setEntryKey(entry); - - // Show the notification if allowed in Preferences - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_key), - getResources().getBoolean(R.bool.keyboard_notification_entry_default))) { - Intent notificationIntent = new Intent(this, KeyboardEntryNotificationService.class); - startService(notificationIntent); - } - } - if (resultCode == Activity.RESULT_CANCELED) { - Log.w(TAG, "Entry not retrieved"); - } - } - finish(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.java index e6340c1db..275ac645c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.java +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardEntryNotificationService.java @@ -136,7 +136,7 @@ public class KeyboardEntryNotificationService extends Service { try { pendingDeleteIntent.send(); } catch (PendingIntent.CanceledException e) { - Log.e(TAG, e.getLocalizedMessage()); + Log.e(TAG, "Unable to delete keyboard notification"); } }); cleanNotificationTimer.start(); diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java index 4fb7c1b55..5d7272189 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java @@ -48,6 +48,7 @@ import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter; import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver; import com.kunzisoft.keepass.magikeyboard.view.MagikeyboardView; import com.kunzisoft.keepass.model.Entry; +import com.kunzisoft.keepass.selection.KeyboardEntryRetrieverActivity; import static com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.LOCK_ACTION; @@ -205,7 +206,7 @@ public class MagikIME extends InputMethodService break; case KEY_ENTRY: deleteEntryKey(this); - Intent intent = new Intent(this, EntryRetrieverActivity.class); + Intent intent = new Intent(this, KeyboardEntryRetrieverActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); break; diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 3be726f77..c6420158b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -341,20 +341,12 @@ public class PasswordActivity extends StylishActivity @Override protected void onResume() { - // If the application was shutdown make sure to clear the password field, if it + // If the database isn't accessible make sure to clear the password field, if it // was saved in the instance state - if (App.isShutdown()) { + if (App.getDB().loaded) { setEmptyViews(); } - // Show message if exists - CharSequence appMessage = App.getMessage(); - if (! appMessage.toString().isEmpty()) - Toast.makeText(this, appMessage, Toast.LENGTH_SHORT).show(); - - // Clear the shutdown flag - App.clearShutdown(); - // For check shutdown super.onResume(); @@ -939,8 +931,6 @@ public class PasswordActivity extends StylishActivity // Clear before we load Database database = App.getDB(); database.clear(getApplicationContext()); - // Clear the shutdown flag - App.clearShutdown(); // Show the progress dialog Handler handler = new Handler(); diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionAuthActivity.java b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionAuthActivity.java deleted file mode 100644 index 349e43ddf..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionAuthActivity.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.selection; - -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; - -import com.kunzisoft.keepass.fileselect.FileSelectActivity; - -public class EntrySelectionAuthActivity extends AppCompatActivity { - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - startFileSelectActivity(); - super.onCreate(savedInstanceState); - } - - protected void startFileSelectActivity() { - // Pass extra to get entry - FileSelectActivity.launchForKeyboardResult(this); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java index ee365981c..71c9db809 100644 --- a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java @@ -31,7 +31,6 @@ public class EntrySelectionHelper { boolean entrySelectionMode = isIntentInEntrySelectionMode(intent); if (entrySelectionMode) { mReplyIntent = new Intent(); - Log.d(activity.getClass().getName(), "Reply entry selection"); Entry entryModel = new Entry(); entryModel.setTitle(entry.getTitle()); @@ -44,6 +43,7 @@ public class EntrySelectionHelper { (key, value) -> entryModel.addCustomField( new Field(key, value.toString()))); } + Log.d(activity.getClass().getName(), "Build entry selection for reply: " + entryModel.getTitle()); mReplyIntent.putExtra( EXTRA_ENTRY_SELECTION_MODE, diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt b/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt new file mode 100644 index 000000000..a6824aaf7 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt @@ -0,0 +1,60 @@ +package com.kunzisoft.keepass.selection + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.preference.PreferenceManager +import android.support.v7.app.AppCompatActivity +import android.util.Log +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.GroupActivity +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.fileselect.FileSelectActivity +import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService +import com.kunzisoft.keepass.magikeyboard.MagikIME +import com.kunzisoft.keepass.model.Entry +import com.kunzisoft.keepass.selection.EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE +import com.kunzisoft.keepass.selection.EntrySelectionHelper.EXTRA_ENTRY_SELECTION_MODE + +class KeyboardEntryRetrieverActivity : AppCompatActivity() { + + companion object { + + val TAG = KeyboardEntryRetrieverActivity::class.java.name!! + } + + override fun onCreate(savedInstanceState: Bundle?) { + if (App.getDB().loaded) + GroupActivity.launchForKeyboardResult(this, true) + else { + // Pass extra to get entry + FileSelectActivity.launchForKeyboardResult(this) + } + + super.onCreate(savedInstanceState) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + Log.d(TAG, "Retrieve the entry selected, requestCode: $requestCode, resultCode: $resultCode") + if (requestCode == ENTRY_SELECTION_RESPONSE_REQUEST_CODE) { + if (resultCode == Activity.RESULT_OK) { + val entry = data?.getParcelableExtra(EXTRA_ENTRY_SELECTION_MODE) + Log.d(TAG, "Set the entry ${entry?.title} to keyboard") + MagikIME.setEntryKey(entry) + + // Show the notification if allowed in Preferences + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_key), + resources.getBoolean(R.bool.keyboard_notification_entry_default))) { + val notificationIntent = Intent(this, KeyboardEntryNotificationService::class.java) + startService(notificationIntent) + } + } + if (resultCode == Activity.RESULT_CANCELED) { + Log.w(TAG, "Entry not retrieved") + } + } + finish() + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java index 68110e9f1..8c7d140d9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java @@ -60,7 +60,7 @@ public class MainPreferenceFragment extends PreferenceFragmentCompat implements preference = findPreference(getString(R.string.db_key)); preference.setOnPreferenceClickListener(this); Database db = App.getDB(); - if (!(db.getLoaded())) { + if (!(db.loaded)) { preference.setEnabled(false); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index 8c1f6f0c7..61f219cd7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -323,7 +323,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat case DATABASE: setPreferencesFromResource(R.xml.database_preferences, rootKey); - if (database.getLoaded()) { + if (database.loaded) { PreferenceCategory dbGeneralPrefCategory = (PreferenceCategory) findPreference(getString(R.string.database_general_key)); diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index 19ea18219..575e2b7a8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -77,16 +77,6 @@ object TimeoutHelper { } } - /** - * Check the time previously record with recordTime and lock the activity if timeout - */ - fun checkTime(activity: Activity): Boolean { - return checkTime(activity) { - if (App.isShutdown() && App.getDB().loaded) - activity.lock() - } - } - /** * Check the time previously record with recordTime and do the shutdown action if timeout */ @@ -127,15 +117,22 @@ object TimeoutHelper { val diff = currentTime - timeoutBackup if (diff >= appTimeout) { // We have timed out - if (App.getDB().loaded) { - App.setShutdown(context.getString(R.string.app_timeout)) - shutdown.invoke() - return false - } + App.getDB().loaded = false + shutdown.invoke() + return false } return true } + /** + * Check the time previously record with recordTime and lock the activity if timeout + */ + fun checkTime(activity: Activity): Boolean { + return checkTime(activity) { + activity.lock() + } + } + fun lockOrResetTimeout(activity: Activity, action: (() -> Unit)? = null) { if (checkTime(activity)) { recordTime(activity) From 0d86fa0d95b58b0e00184a5613659249ce8d9e10 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 19 Dec 2018 22:38:43 +0100 Subject: [PATCH 041/289] Fix infinite time with keyboard and autofill --- .../java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt | 2 ++ .../keepass/selection/KeyboardEntryRetrieverActivity.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt index fed3c49c6..aa92f16e4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt @@ -31,11 +31,13 @@ import android.support.v7.app.AppCompatActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.fileselect.FileSelectActivity +import com.kunzisoft.keepass.timeout.TimeoutHelper @RequiresApi(api = Build.VERSION_CODES.O) class AutoFillAuthActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { + TimeoutHelper.checkTime(this) {} // Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE) val assistStructure = AutofillHelper().retrieveAssistStructure(intent) if (assistStructure != null) { diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt b/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt index a6824aaf7..8d4c37490 100644 --- a/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt @@ -15,6 +15,7 @@ import com.kunzisoft.keepass.magikeyboard.MagikIME import com.kunzisoft.keepass.model.Entry import com.kunzisoft.keepass.selection.EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE import com.kunzisoft.keepass.selection.EntrySelectionHelper.EXTRA_ENTRY_SELECTION_MODE +import com.kunzisoft.keepass.timeout.TimeoutHelper class KeyboardEntryRetrieverActivity : AppCompatActivity() { @@ -24,6 +25,7 @@ class KeyboardEntryRetrieverActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { + TimeoutHelper.checkTime(this) {} if (App.getDB().loaded) GroupActivity.launchForKeyboardResult(this, true) else { From 0f210408887ac4de3be7124c9032c1f990b10b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89frit?= Date: Wed, 26 Dec 2018 22:13:14 +0000 Subject: [PATCH 042/289] Translated using Weblate (French) Currently translated at 99.7% (344 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 383 ++++++++++++------------- 1 file changed, 191 insertions(+), 192 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9456ac1aa..465012daa 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,4 +1,4 @@ - + - - Signaler une anomalie - Site web - Implémentation Android du gestionnaire de mots de passe KeePass. +--> + Signaler un bogue + Site Web + Implémentation Android du gestionnaire de mots de passe KeePass Accepter Ajouter une entrée Ajouter un groupe @@ -28,136 +27,136 @@ Chiffrement Algorithme de chiffrement Fonction de dérivation de clé - Délai d\'expiration de l\'application - Inactivité avant le verrouillage de l\'application + Délai d’expiration de l’application + Durée d’inactivité avant le verrouillage de la base de données Application - Préférences de l\'application + Paramètres de l’application Remplissage de formulaire Ne plus afficher Crochets ASCII étendu - Parcourez les fichiers en installant le gestionnaire de fichiers OpenIntents + Parcourir les fichiers en installant le gestionnaire de fichiers OpenIntents Annuler - Permettre + Autoriser Presse-papier vidé Erreur de presse-papier - Certains appareils Android Samsung ne permettent pas aux applications d\'utiliser le presse-papier. - Impossible d\'effacer le presse-papier + Certains appareils Android Samsung ne permettent pas aux applications d’utiliser le presse-papier. + Impossible de vider le presse-papier Expiration du presse-papier Durée de stockage dans le presse-papier - Balayez pour effacer le presse-papiers maintenant + Balayer pour vider le presse-papier maintenant Copier %1$s dans le presse-papier - Création de la clé de base de données… + Récupération de la clé de la base de données… Base de données Déchiffrement du contenu de la base de données… Utiliser comme base de données par défaut - Nombres - KeePass DX © %1$d Kunzisoft n\'offre ABSOLUMENT AUCUNE GARANTIE ; il s\'agit d\'un logiciel libre, vous pouvez le redistribuer sous les conditions de la licence GPL v3 ou ultérieure. + Chiffres + KeePass DX © %1$d Kunzisoft n’offre ABSOLUMENT AUCUNE GARANTIE. Il s’agit d’un logiciel libre, vous êtes donc encouragé à le redistribuer sous les conditions de la licence GPL version 3 ou ultérieure. Dernier accès Annuler Notes - Confirmer mot de passe - Créé + Confirmer le mot de passe + Créée Expire Fichier clé - Modifié - Impossible de trouver les données d\'entrée. + Modifiée + Impossible de trouver les données de l’entrée. Mot de passe Enregistrer Titre URL - Nom d\'utilisateur - Le chiffrement de flux ARCFOUR n\'est pas supporté. + Nom d’utilisateur + Le chiffrement de flux ARCFOUR n’est pas pris en charge. Impossible de gérer cette URI dans KeePass DX. - La création du groupe parent a échoué. - Le fichier existe déjà. - Échec d\'ouverture du lien. - Saisissez un nom de fichier. - Impossible de créer le fichier : + Impossible de créer le répertoire parent. + Ce fichier existe déjà. + Impossible d’ouvrir le lien. + Saisir le nom du fichier. + Impossible de créer le fichier : Impossible de lire la base de données. - Assurez-vous de la validité du chemin. - Saissisez un nom. - Sélectionnez un fichier clé. - Pas de mémoire pour charger l\'ensemble de votre base de données. + Vérifier la validité du chemin. + Saisir un nom. + Sélectionner un fichier clé. + Mémoire insuffisante pour charger l’ensemble de votre base de données. Au moins un type de génération de mot de passe doit être sélectionné. Les mots de passe ne correspondent pas. - Faites de \"Niveaux\" un nombre. - \"Niveaux\" trop haut. Réglé à 2147483648. - Un nom de champ est requis pour chaque élément. + « Tours de transformation » doit être un nombre. + « Tours de transformation » trop grand. Défini à 2147483648. + Chaque chaîne doit avoir un nom de champ. Ajoutez un titre. - Entrez un entier positif dans le champ \"Longueur\". - Impossible d\'activer le service de remplissage automatique. + Saisir un entier positif dans le champ « Longueur ». + Impossible d’activer le service de remplissage automatique. Nom du champ Valeur du champ Impossible de trouver le fichier. - Impossible de trouver le fichier. Essayez de l\'ouvrir à nouveau à partir de votre navigateur de fichiers. - Navigateur de fichiers - Générer mot de passe - confirmez mot de passe - mot de passe généré - Nom de groupe + Impossible de trouver le fichier. Essayer de le rouvrir depuis votre gestionnaire de fichiers. + Gestionnaire de fichiers + Générer un mot de passe + Confirmer le mot de passe + Mot de passe généré + Nom du groupe Fichier clé longueur mot de passe Mot de passe Installer depuis Google Play Installer depuis F-Droid - Mot de passe ou fichier de clé invalide. + Impossible de lire le mot de passe ou le fichier clé. Mauvais algorithme. Impossible de reconnaître le format de la base de données. - Le fichier de clé n\'existe pas. + Aucun fichier de clé n’existe. Le fichier de clé est vide. Longueur Taille des éléments de liste - Taille de la police de caractères utilisée pour les éléments de liste - Ouverture de la base de données… + Taille du texte des éléments de liste + Chargement de la base de données… Minuscule - Masquer les mots de passe + Cacher les mots de passe Masquer les mots de passe (***) par défaut À propos - Modifier la clé maître + Modifier la clé maîtresse Copie de %1$s - Préférences - Préférences de la base de données + Paramètres + Paramètres de la base de données Supprimer Faire un don Modifier - Masquer le mot de passe + Cacher le mot de passe Verrouiller la base de données Ouvrir Rechercher Afficher le mot de passe - Supprimer l\'empreinte digitale enregistrée - Ouvrir l\'URL + Supprimer l’empreinte digitale enregistrée + Ouvrir l’URL Moins Jamais Aucun résultat pour cette recherche - Installez un navigateur web pour ouvrir cette URL. - Ouvvrir une base de données existante + Installer un navigateur web pour ouvrir cette URL. + 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) - Création d\'une nouvelle base de données… - Veuillez patienter… + Omet le groupe « Sauvegarde » des résultats de recherche (ne s’applique qu’aux fichiers .kdb) + Création d’une nouvelle base de données… + Prière de patienter… Protection Protégé en écriture - KeePass DX a besoin d\'une autorisation d\'écriture pour pouvoir modifier quoi que ce soit dans votre base de données. - À partir d\'Android KitKat, certains appareils n\'autorisent plus les applications à écrire sur la carte SD. + KeePass DX a besoin de la permission d’écriture afin modifier la base de données. + À partir d’Android KitKat, certains appareils n’autorisent plus les applications à écrire sur la carte microSD. Historique de fichiers récents - Mémoriser les noms de fichiers récents - Mémoriser l\'emplacement des fichiers clés des bases de données - Mémoriser le fichier de clé + Mémoriser les noms de fichier récents + Mémoriser l’emplacement des fichiers clé des bases de données + Enregistrer le fichier clé Effacer Racine Algorithme de chiffrement de la base de données utilisé pour toutes les données. - Afin de générer la clé pour l\'algorithme de chiffrement, la clé maîtresse est transformée en utilisant une fonction de dérivation de clé salée aléatoirement. + Afin de générer la clé pour l’algorithme de chiffrement, la clé maîtresse est transformée en utilisant une fonction de dérivation de clé salée aléatoirement. Tours de transformation - Un niveau de chiffrement supplémentaire assure une protection plus élevée contre les attaques de force brute, mais peut considérablement ralentir l\'ouverture et l\'enregistrement. - niveaux + Des tours de chiffrement supplémentaires fournissent une protection plus élevée contre les attaques par force brute, mais cela peut considérablement ralentir les opérations de chargement et d’enregistrement. + tours de transformation Utilisation de la mémoire - Quantité de mémoire (en octets binaires) à utiliser par la fonction de dérivation de clé. + Quantité de mémoire (en multiplets binaires) à utiliser par la fonction de dérivation de clé. Parallélisme - Degré de parallélisme (nombre de threads) utilisé par la fonction de dérivation de clé. + Degré de parallélisme (nombre de fils d’exécution) utilisé par la fonction de dérivation de clé. Enregistrement de la base de données… Espace Rechercher @@ -167,89 +166,89 @@ Corbeille à la fin Tri par défaut (base de données) Titre - Nom d\'utilisateur + Nom d’utilisateur Création Modification Accès Spécial - Nom/description - Résultats + Rechercher + Résultats de recherche Souligné - Version de la base de données non supportée. + Version de la base de données non prise en charge. Majuscule - Utiliser l\'environnement d\'accès au stockage (SAF) Android pour naviguer dans les fichiers (KitKat ou plus récent) - Environnement d\'accès au stockage + Utiliser l’environnement de développement d’accès au stockage (SAF) Android pour naviguer dans les fichiers (à partir de KitKat) + Environnement de développement d’accès au stockage Alerte - Évitez les caractères de mot de passe en dehors de Latin-1 dans les fichiers .kbd car ils sont tous convertis dans la même lettre. - Accordez l\'accès en écriture à la carte SD pour enregistrer les modifications apportées à la base de données. - Montez la carte SD pour créer ou charger une base de données. - Voulez-vous vraiment pas de protection de déverouillage par mot de passe \? - Êtes-vous sûr de ne vouloir utiliser aucune clé de chiffrement ? + Éviter les caractères en dehors de Latin-1 pour les mots de passe dans les fichiers .kbd car ils sont tous convertis en une même lettre. + Accorder l’accès en écriture à la carte microSD pour enregistrer les modifications de la base de données. + Monter la carte microSD pour créer ou charger une base de données. + Ne voulez-vous vraiment aucune protection de déverrouillage par mot de passe \? + Êtes-vous sûr de ne vouloir utiliser aucune clé de chiffrement \? Version %1$s - La reconnaissance d\'empreinte digitale est supportée mais non configurée. - Reconnaissance d\'empreinte digitale - Mot de passe encrypté stocké - Impossible de lire les empreintes digitales. Restaurez votre mot de passe. - Impossible de reconnaître les empreintes digitales - Problème d\'empreintes digitales : %1$s - Utiliser l\'empreinte digitale pour stocker le mot de passe - Cette base de données n\'a pas encore de mot de passe. + La reconnaissance d\'empreinte digitale est prise en charge mais pas configurée. + Reconnaissance d’empreinte digitale + Mot de passe chiffré stocké + Impossible de lire la clé de l’empreinte digitale. Restaurer votre mot de passe. + Impossible de reconnaître l’empreinte digitale + Problème d’empreinte digitale : %1$s + Utiliser l’empreinte digitale pour stocker ce mot de passe + Cette base de données n’a pas encore de mot de passe. Historique Apparence - Générale + Général Remplissage automatique Remplissage automatique des formulaires KeePass DX Se connecter avec KeePass DX Définir le service de remplissage automatique par défaut - Activer le remplissage automatique pour remplir rapidement des formulaires dans d\'autres applications + Activer le remplissage automatique pour remplir rapidement des formulaires dans d’autres applications Taille du mot de passe généré Définir la taille par défaut des mots de passe générés Caractères de mot de passe Définir les caractères autorisés du générateur de mot de passe - Presse-papiers - Notifications du presse-papiers - Activer les notifications du presse-papiers pour copier les champs d\'entrées - Si la suppression automatique du presse-papier échoue, supprimez son historique manuellement. + Presse-papier + Notifications du presse-papier + Activer les notifications du presse-papier pour copier les champs des entrées + Si la suppression automatique du presse-papier échoue, supprimer son historique manuellement. Verrouiller - Verrouillage d\'écran - Verrouiller la base de données quand l\'écran est éteint - Comment configurer l\'empreinte digitale pour un déverrouillage rapide ? - Enregistrez votre empreinte digitale pour votre appareil dans - \"Paramètres\" → \"Sécurité\" → \"Empreinte digitale\" - Entrez le mot de passe de verrouillage de la base de données - Scannez votre empreinte digitale pour stocker le mot de passe de votre base de données en toute sécurité. - Scannez votre empreinte digitale pour ouvrir la base de données lorsque le mot de passe est désactivé. - Usage + Verrouillage d’écran + Verrouiller la base de données lorsque l’écran est éteint + Comment configurer la reconaissance de l’empreinte digitale pour un déverrouillage rapide \? + Enregistrer votre empreinte digitale pour votre appareil dans + « Paramètres » → « Sécurité » → « Empreinte digitale » + Saisir le mot de passe de verrouillage de la base de données + Numériser votre empreinte digitale pour stocker le mot de passe de verrouillage de votre base de données en sécurité. + Numériser votre empreinte digitale pour ouvrir la base de données lorsque le mot de passe est désactivé. + Utilisation Empreinte digitale - Reconnaissance d\'empreintes digitales - Vous permet de scanner vos empreintes digitales pour ouvrir la base de données + Reconnaissance d’empreinte digitale + Vous permet de numériser votre empreinte digitale pour ouvrir la base de données Supprimer les clés de chiffrement - Supprimer toutes les clés de chiffrement liées à la reconnaissance des empreintes digitales - Êtes-vous sûr de vouloir supprimer toutes les clés relatives à la reconnaissance d\'empreintes digitales \? + Supprimer toutes les clés de chiffrement liées à la reconnaissance d’empreinte digitale + Êtes-vous sûr de vouloir supprimer toutes les clés liées à la reconnaissance d’empreinte digitale \? Impossible de démarrer cette fonctionnalité. - Votre version Android %1$s n\'est pas la version minimale %2$s requise. + Votre version Android %1$s est inférieure à la version minimale %2$s requise. Impossible de trouver le matériel correspondant. Nom de fichier Chemin - Assigner une clé maître + Assigner une clé maîtresse Créer une nouvelle base de données - Octets + Multiplets Chemin de fichier - Afficher le chemin complet des fichiers + Voir le chemin de fichier complet Utiliser la corbeille - Déplace les groupes et entrées dans la \"Corbeille\" avant la suppression - KeePass DX a besoin d\'une permission de stockage externe pour écrire dans une base de données. - KeePass DX a besoin d\'une permission de stockage externe pour lire une URI non fournie par un fournisseur de contenu. - Impossible d\'obtenir la permission de stockage externe. - Impossible d\'effectuer l\'action sans permission de stockage externe. + Déplace les groupes et les entrées dans la « Corbeille » avant la suppression + KeePass DX a besoin de la permission de stockage externe pour écrire dans une base de données. + KeePass DX a besoin de la permission de stockage externe pour lire une URI non fournie par un fournisseur de contenu. + Impossible d’obtenir la permission de stockage externe. + Impossible d’effectuer l’action sans la permission de stockage externe. Police de champ Changer la police utilisée dans les champs pour une meilleure visibilité des caractères - Ouvrir les fichiers en sélectionnant - Ouvrir automatiquement les fichiers lorsqu\'ils sont sélectionnés dans le navigateur de fichiers + Ouvrir les fichiers par sélection + Ouvrir automatiquement les fichiers lorsqu’ils sont sélectionnés dans le gestionnaire de fichiers Confiance dans le presse-papier - Autoriser le mot de passe de l\'entrée et les champs protégées à entrer dans le presse-papier - ATTENTION : Le presse-papiers est partagé par toutes les applications. Si des données sensibles sont copiés, d\'autres logiciels peuvent les récupérer. - ATTENTION : La désactivation de cette fonction peut empêcher l\'ouverture ou la sauvegarde des bases de données. + Autoriser le mot de passe de l’entrée et les champs protégés à entrer dans le presse-papier + ATTENTION : le presse-papier est partagé par toutes les applications. Si des données sensibles sont copiées, d’autres logiciels peuvent les récupérer. + ATTENTION : la désactivation de cette fonctionnalité peut empêcher l’ouverture ou l’enregistrement des bases de données. Lien du fichier de base de données à ouvrir Nom de la base de données Description de la base de données @@ -259,60 +258,60 @@ Autres Clavier Magikeyboard - Activer un clavier personnalisé pour remplir vos mots de passe et tous les champs d\'identité + Activer un clavier personnalisé pour remplir vos mots de passe et tous les champs d’identité - Ecrans éducatifs - Met en surbrillance les éléments pour apprendre le fonctionnement de l\'application + Écrans éducatifs + Met en surbrillance les éléments pour apprendre le fonctionnement de l’application Réinitialiser les écrans éducatifs Montrer à nouveau tous les éléments éducatifs - Ecrans éducatifs réinitialisés - Créez votre fichier de base de données - Créez votre premier fichier de gestion de mot de passe. - Ouvrez une base de données existante - Ouvrez votre ancien fichier de base de données à partir de votre navigateur de fichiers pour continuer à l\'utiliser. - Un lien vers l\'emplacement de votre fichier suffit - Vous pouvez aussi ouvrir votre base avec un lien physique. (avec file:// et content:// par exemple). - Ajoutez des éléments à votre base de données - Les entrées vous aident à gérer vos identités numériques. + Écrans éducatifs réinitialisés + Créer votre fichier de base de données + Créer votre premier fichier de gestion de mots de passe. + Ouvrir une base de données existante + Ouvrir votre ancien fichier de base de données depuis votre gestionnaire de fichiers pour continuer à l’utiliser. + Un lien vers l’emplacement de votre fichier suffit + Vous pouvez aussi ouvrir votre base de données avec un lien physique (avec file:// et content:// par exemple). + Ajouter des éléments à votre base de données + Les entrées aident à gérer vos identités numériques. \n -\nLes groupes (~dossiers) organisent les entrées dans votre base de données. - Recherchez dans les entrées - Entrez le titre, le nom d\'utilisateur ou le contenu des autres champs pour retrouver vos mots de passe. - Déverouillage de la base de données par empreintes digitales - Associez votre mot de passe à vos empreintes digitales scannées pour déverouiller rapidement votre base de données. - Editez l\'entrée - Éditez votre entrée avec des champs personnalisés. La collection de données peut être référencée entre différents champs de l\'entrée. - Créez un mot de passe fort pour votre entrée. - Générez un mot de passe fort à associer avec votre entrée, définissez le facilement en fonction des critères du formulaire et n\'oubliez pas le mot de passe sécurisé. - Ajoutez des champs customisés - Enregistrez un champ de base non fourni en remplissant un nouveau champ que vous pouvez égalemement protéger. - Débloquez votre base de données - Entrez un mot de passe et/ou un fichier de clé pour ouvrir la base de données. +\nLes groupes (~ dossiers) organisent les entrées dans votre base de données. + Rechercher dans les entrées + Entrer le titre, le nom d’utilisateur ou le contenu des autres champs pour récupérer vos mots de passe. + Déverrouillage de la base de données par empreinte digitale + Associer votre mot de passe à votre empreinte digitale numérisée pour déverrouiller rapidement votre base de données. + Modifier l’entrée + Modifier votre entrée avec des champs personnalisés. La collection de données peut être référencée entre différents champs de l’entrée. + Créer un mot de passe fort pour votre entrée. + Générer un mot de passe fort à associer avec votre entrée, définir facilement en fonction des critères du formulaire et ne pas oublier le mot de passe sécurisé. + Ajoutez des champs personnalisés + Enregistrer un champ additionnel, ajouter une valeur et facultativement le protéger. + Déverrouiller votre base de données + Saisir le mot de passe ou le fichier de clé pour déverrouiller votre base de données. \n -\nPensez à sauvegarder une copie de votre fichier .kdbx en lieu sûr après chaque modification. - Copiez un champ - Copiez un champ facilement pour le coller où vous voulez. +\nSauvegarder une copie de votre fichier de base de données en lieu sûr après chaque modification. + Copier un champ + Les champs copiés peuvent être collés n’importe où. \n -\n Vous pouvez utilisez plusieurs méthodes de remplissage de formulaire. Utilisez celle que vous préférez. - Vérouillez la base de données - Verrouillez votre base de données rapidement, vous pouvez paramétrer l\'application pour qu\'elle se verrouille après un certain temps et lorsque l\'écran s\'éteint. +\nUtiliser la méthode de remplissage automatique de formulaire que vous préférez. + Verrouiller la base de données + Verrouillez votre base de données rapidement. Vous pouvez configurer l’application pour qu’elle la verrouille après un délai et lorsque l’écran s’éteint. Tri des éléments - Choississez comment les entrées et les groupes sont triés. - Participez - Aidez à améliorer la stabilité, la sécurité et à ajouter plus de fonctionnalités. + Choisir comment les entrées et les groupes sont triés. + Participer + Aider à améliorer la stabilité, la sécurité et à ajouter des fonctionnalités. - Contrairement à d\'autres applications de gestion de mots de passe, cette application est sans publicité, libre sous licence copyleft et ne collecte pas de données personnelles sur ses serveurs, peu importe la version que vous utilisez. + Contrairement à beaucoup d’applications de gestion de mots de passe, cette application est sans publicité, libre sous licence copyleft et ne collecte pas de données personnelles sur ses serveurs, peu importe la version que vous utilisez. En achetant la version pro, vous accéderez à cette <strong>fonctionnalité visuelle</strong> et vous aiderez en particulier à <strong>la réalisation de projets communautaires.</strong> Cette <strong>fonctionnalité visuelle</strong> est disponible grâce à votre générosité. Afin de garder notre liberté et de toujours être actifs, nous comptons sur votre contribution. - Cette fonctionnalité est <strong>en cours de développement</strong> et nécessite votre <strong>contribution</strong> pour être prochainement disponible. + Cette fonctionnalité est en cours de développement et nécessite votre contribution pour être bientôt disponible. En achetant la version <strong>pro</strong>, En <strong>contribuant</strong>, - vous encouragez les développeurs à créer de <strong>nouvelles fonctionnalités</strong> et à <strong>résoudre des bugs</strong> en fonction de vos remarques. + vous encouragez les développeurs à créer de nouvelles fonctionnalités et à corriger des bogues en fonction de vos remarques. Merci beaucoup pour votre contribution. - Nous travaillons dur pour sortir cette fonctionnalité rapidement. - N\'oubliez pas de garder votre application à jour en installant les nouvelles versions. + Nous travaillons dur pour livrer cette fonctionnalité rapidement. + N’oubliez pas de garder votre application à jour en installant les nouvelles versions. Télécharger Contribuer @@ -323,7 +322,7 @@ ChaCha20 - AES KDF + Fonction de dérivation de clé AES Argon2 @@ -343,8 +342,8 @@ Grand - Thème de l\'application - Thème utilisé dans l\'application + Thème de l’application + Thème utilisé dans l’application Thème Jour Thème Nuit @@ -353,63 +352,63 @@ Thème Rouge Volcan Thème Pro Violet - Pack d\'icônes - Pack d\'icônes utilisé dans l\'application + Paquet d’icônes + Paquet d’icônes utilisé dans l’application -Vous ne pouvez pas déplacer un groupe en lui-même. +Vous ne pouvez pas déplacer un groupe dans lui-même. Copier Déplacer Coller Annuler Paramètres du Magikeyboard - Configurer le clavier pour le remplissagee automatique des formulaires en toute sécurité. - Activez le \"Magikeyboard\" dans les paramètres de l\'appareil. - \"Paramètres\" → \"Langue & saisie\" → \"Clavier actuel\" et en choisir un. - ou (\"Paramètres\" → \"Système\" → \"Langues et saisie\" → \"Clavier virtuel\" et en choisir un.) + Configurer le clavier pour le remplissage automatique des formulaires en sécurité. + Activer le « Magikeyboard » dans les paramètres de l’appareil. + « Paramètres » → « Langues et saisie » → « Clavier actuel » et en choisir un. + (Ou « Paramètres » → « Langues et saisie » → « Clavier virtuel » et en choisir un.) Choisir le Magikeyboard lorsque vous avez besoin de remplir un formulaire. - Changez de clavier en appuyant longuement sur la barre espace de votre clavier, ou, si ce n\'est pas disponible, avec : - Sélectionnez votre entrée avec la clé. - Remplissez vos champs avec les éléments de l\'entrée. + Changer de clavier en appuyant longuement sur la barre d’espace de votre clavier ou, si ce n’est pas disponible, avec : + Sélectionner votre entrée avec la clé. + Remplisser vos champs avec les éléments de l’entrée. Verrouiller la base de données. Utiliser à nouveau le clavier par défaut. - Autoriser aucun mot de passe - Activer le bouton \"ouvrir\" si aucune identification de mot de passe n\'est sélectionnée + N’autoriser aucun mot de passe + Activer le bouton « Ouvrir » si aucune identification par mot de passe n’est sélectionnée Protégé en écriture Modifiable Protégé en écriture Ouvrir votre base de données en lecture seule par défaut - Protégez en écriture votre base de données - Changez le mode d\'ouverture de la session. -\n -\n\"Protégé en écriture\" empêche les modifications involontaires de la base de données. -\n -\n\"Modifiable\" vous permet d\'ajouter, supprimer ou modifier tous les éléments. - Modifier l\'entrée - Impossible de chager votre base de données. - Impossible de charger la clé. Essayez de diminuer la mémoire utilisée par la KDF. - Afficher les noms d\'utilisateur - Afficher les noms d\'utilisateur dans les listes d\'entrées - Construire %1$s + Protéger en écriture votre base de données + Changer le mode d’ouverture pour la session. +\n +\n« Protégé en écriture » empêche les modifications involontaires de la base de données. +\n +\n« Modifiable » vous permet d’ajouter, de supprimer ou de modifier tous les éléments. + Modifier l’entrée + Impossible de charger votre base de données. + Impossible de charger la clé. Essayer de diminuer la mémoire utilisée par la fonction de dérivation de clé. + Afficher les noms d’utilisateur + Afficher les noms d’utilisateur dans les listes des entrées + Compiler %1$s Magikeyboard Magikeyboard (KeePass DX) Paramètres Magikeyboard Entrée - Délai d\'attente - Délai d\'attente pour effacer l\'entrée au clavier + Délai d’expiration + Délai d’expiration pour effacer l’entrée au clavier Informations de notification - Afficher une notification lorsqu\'une entrée est disponible + Afficher une notification lorsqu’une entrée est disponible Entrée 1$s disponible sur Magikeyboard %1$s Effacer à la fermeture - Effacer l\'entrée du clavier lors de la fermeture de la notification + Effacer l’entrée au clavier lors de la fermeture de la notification Apparence Thème du clavier @@ -417,4 +416,4 @@ Touches Vibrer au toucher Son au toucher - + \ No newline at end of file From 4e8f554ae69c5bd52a3271f9cec05caeb96c6512 Mon Sep 17 00:00:00 2001 From: Ldm Public Date: Thu, 27 Dec 2018 21:32:56 +0000 Subject: [PATCH 043/289] Translated using Weblate (French) Currently translated at 99.7% (344 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 465012daa..2c691cff4 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -108,10 +108,10 @@ Le fichier de clé est vide. Longueur Taille des éléments de liste - Taille du texte des éléments de liste + Taille du texte dans les éléments de liste Chargement de la base de données… Minuscule - Cacher les mots de passe + Masquer les mots de passe Masquer les mots de passe (***) par défaut À propos Modifier la clé maîtresse @@ -121,7 +121,7 @@ Supprimer Faire un don Modifier - Cacher le mot de passe + Masquer le mot de passe Verrouiller la base de données Ouvrir Rechercher @@ -137,13 +137,13 @@ 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) Création d’une nouvelle base de données… - Prière de patienter… + Traitement en cours … Protection Protégé en écriture KeePass DX a besoin de la permission d’écriture afin modifier la base de données. À partir d’Android KitKat, certains appareils n’autorisent plus les applications à écrire sur la carte microSD. Historique de fichiers récents - Mémoriser les noms de fichier récents + Mémoriser les noms des fichiers récents Mémoriser l’emplacement des fichiers clé des bases de données Enregistrer le fichier clé Effacer @@ -234,9 +234,9 @@ Créer une nouvelle base de données Multiplets Chemin de fichier - Voir le chemin de fichier complet + Afficher le chemin complet du fichier Utiliser la corbeille - Déplace les groupes et les entrées dans la « Corbeille » avant la suppression + Déplace les groupes et les entrées dans la « Corbeille » avant suppression KeePass DX a besoin de la permission de stockage externe pour écrire dans une base de données. KeePass DX a besoin de la permission de stockage externe pour lire une URI non fournie par un fournisseur de contenu. Impossible d’obtenir la permission de stockage externe. @@ -352,8 +352,8 @@ Thème Rouge Volcan Thème Pro Violet - Paquet d’icônes - Paquet d’icônes utilisé dans l’application + Ensemble d’icônes + Ensemble d’icônes utilisés dans l’application Vous ne pouvez pas déplacer un groupe dans lui-même. Copier @@ -366,7 +366,7 @@ « Paramètres » → « Langues et saisie » → « Clavier actuel » et en choisir un. (Ou « Paramètres » → « Langues et saisie » → « Clavier virtuel » et en choisir un.) Choisir le Magikeyboard lorsque vous avez besoin de remplir un formulaire. - Changer de clavier en appuyant longuement sur la barre d’espace de votre clavier ou, si ce n’est pas disponible, avec : + Basculer de clavier en appuyant longuement sur la barre d’espace de votre clavier ou, si ce n’est pas disponible, avec : Sélectionner votre entrée avec la clé. Remplisser vos champs avec les éléments de l’entrée. Verrouiller la base de données. From 2046e15f65c4855dd5fbf04d5154f1b8be33fed9 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Sun, 30 Dec 2018 16:19:21 +0000 Subject: [PATCH 044/289] Translated using Weblate (Basque) Currently translated at 21.4% (74 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/eu/ --- app/src/main/res/values-eu/strings.xml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 9692aa152..fa4be2849 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -18,15 +18,14 @@ along with KeePass DX. If not, see . Basque translation by David García-Abad, released under GPL V2. ---> - +--> Feedback: - Hasiera orria: - KeePass DX ireki iturburukoa den Keepass pasahitza kudeatzailaren Androiderako inplementazioa da. + Hasiera orria + Keepass pasahitza kudeatzailearen Androiderako inplementazioa Onartu Sarrera gehitu Taldea gehitu - Algoritmoa + Zifratze algoritmoa Aplikazioa itzaltzeko denbora Denbora datubasea blokeatu baino lehenago aplikazioa erabili gabe dagoenean Aplikazioa @@ -42,9 +41,9 @@ Arbela denbora pasatu da Erabiltzaile izena edo pasahitza kopiatu ondoren arbela ezabatzeko igaro beharreko denbora Aukeratu %1$s arbelera kopiatzeko - Datubasearen gakoa sortzen… + Datubasearen gakoa sortzen… Datubasea - Datubasearen datuak desenkriptatzen… + Datubasearen datuak desenkriptatzen… Hau erabili modu lehenetsitako datubase gisa Zenbakiak KeePass DX \u00A9 %1$d Kunzisoft ez dakar inolako bermerik; Lan hau software librea da; banatu edo/eta aldatu egin dezakezu GNU General Public License bigarren bertsioaren baldintzapean. @@ -104,7 +103,7 @@ Luzera Talde listaren tamaina Testuaren tamaina taldearen listan - Datubasea kargatzen… + Datubasea kargatzen… minuskulak Pasahitza estali Pasahitza estali modu lehenetsian @@ -128,8 +127,8 @@ Duela gutxiko datubasea ireki : Ez bilatu segurtasun kopiaren sarreretan Kendu segurtasun kopien taldea bilaketen emaitzetatik (.kdb fitxategie dagokie bakarrik) - Datubase berria sortzen… - Lanean… + Datubase berria sortzen… + Lanean… Babesa Duela gutxiko fitxategien historia Gogoratu duela gutxi erabili diren fitxategien izenak @@ -141,7 +140,7 @@ Enkriptatzeko Rondak Enkriptatzeko ronda gehiago indar gordineko atakeen kontrako babes gehiago ematen dute, baina kargatzea eta gordetzea moteldu dezakete modu nabarmenean. rondak - Datubasea gordetzen… + Datubasea gordetzen… Lekua Bilatu DB-a antolatzeko ordena @@ -173,4 +172,4 @@ Ertaina Handia - + \ No newline at end of file From e3f3a5faf7ecf61143af94b0e486a92827df5930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Mon, 7 Jan 2019 18:55:37 +0000 Subject: [PATCH 045/289] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 98.6% (340 of 345 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 | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 188f1a507..4c45c4b79 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -1,8 +1,8 @@ - + Tilbakemelding: Hjemmeside: - KeePass DX er en Android-implementasjon av KeePass-passordsbehandleren. + Android-implementasjon av KeePass-passordsbehandleren Godta Legg til oppføring Rediger oppføring @@ -17,12 +17,12 @@ Ikke vis igjen Parenteser Utvidet ASCII - Filutforskning krever at OpenIntents filutforsker er installert, klikk nedenfor for å gjøre dette. På grunn av noen snodigheter i filutforskeren, kan det hende at det ikke virker første gang. + Utforsk filer ved å installere OpenIntents-filbehandleren Avbryt Tillat Utklippstavle tømt Utklippstavlefeil - Noen Samsung Android-telefoner har en feil i utklippstavlen, som forårsaker problemer ved kopiering fra programmer. Flere detaljer: + Noen Android-telefoner fra Samsung lar ikke programmer bruke utklippstavlen. Tømming av utklippstavle mislyktes Tidsavbrudd for utklippstavle Tid før utklippstavlen tømmes etter kopiering av brukernavn og passord @@ -60,7 +60,7 @@ Et navn er påkrevd. En nøkkelfil er påkrevd. Enheten slapp opp for minne under fortolkning av databasen din. - Databasen kan ikke lastes + Kunne ikke laste inn databasen din. Kunne ikke laste nøkkelen, prøv å senke minnet brukt av KDF. Minst én passordgenereringstype må velges. Passordene samsvarer ikke. @@ -187,7 +187,7 @@ Fremmed fingeravtrykk Fingeravtrykksproblem: %1$s Bruk fingeravtrykk til å lagre dette passordet - Inget passord lagret for denne databasen enda + Denne databasen har ikke et passord enda. Historikk Utseende Generelt @@ -212,7 +212,7 @@ \"Innstillinger\" → \"Sikkerhet\" → \"Fingeravtrykk\" Skriv passordet ditt i KeePass DX Skann ditt fingeravtrykk for å lagre hovedpassordet ditt sikkert - Skann fingeravtrykket ditt når passordutkryssingsboksen er fravalgt for å åpne databasen + Skann fingeravtrykket ditt for å åpne databasen når passord er avskrudd. Bruk Fingeravtrykk Skanner etter fingeravtrykk @@ -241,7 +241,7 @@ Åpne valgt fil automatisk Åpne en fil fra utvalgsskjermen automatisk etter at et valg er gjort i filutforskeren Kopi av passord - Tillat kopiering av passordet og beskyttede felter til utklippstavlen. + Tillat kopiering av adgangspassordet og beskyttede felter til utklippstavlen. ADVARSEL: Utklippstavlen deles av alle programmer. Hvis sensitiv data kopieres, kan annen programvare gjenopprette den. ADVARSEL: Å skru av denne funksjonen kan lede til manglende evne til å åpne eller lagre databaser. Lenke til KDBD-filen å åpne @@ -254,9 +254,9 @@ Tastatur Magikeyboard - Aktiver et egendefinert tastatur som fyller inn passordene og alle identitetsfelter automatisk. + Aktiver et egendefinert tastatur som fyller inn passordene og alle identitetsfelter. Magikeyboard-innstillinger - Hvordan setter jeg opp tastaturet for sikker skjemautfylling? + Sett opp tastaturet for sikker skjemautfylling. Aktiver Magikeyboard i enhetsinnstillingene. \"Innstillinger\" → \"Språk og inndata\" → \"Nåværende tastatur\" og velg ett. eller (\"Innstillinger\" → \"Språk og inndata\" → \"Virtuelt tastatur\" og velg ett.) @@ -270,7 +270,7 @@ Tillat inget passord Skru på \"Åpne\"-tasten hvis ingen passordidentifikasjon er valgt. Skrivebeskyttet - Åpne databaser skrivebeskyttet som forvalg. + "Åpne din database skrivebeskyttet som forvalg." Hjelpeskjermer Framhev elementer for opplæring i programmet @@ -370,4 +370,4 @@ Taster Vibrer ved tastetrykk Lyd ved tastetrykk - + \ No newline at end of file From f54b908dbbf4f79143c074175298aeae57d04a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hadri=C3=A1n=20Candela?= Date: Fri, 11 Jan 2019 10:02:43 +0000 Subject: [PATCH 046/289] Translated using Weblate (Galician) Currently translated at 8.7% (30 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/gl/ --- app/src/main/res/values-gl/strings.xml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 9636f8a09..b32cd2007 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1,4 +1,4 @@ - + Comentarios: Páxina inicial: KeePass DX é unha implementación para Android do xestor de contrasinais KeePass. @@ -23,4 +23,21 @@ Tempo límite para o portapapeis Tempo antes de limpar o portapapeis após copiar usuario ou contrasinal Deslice para limpar agora o portapapeis - + Editar entrada + Seleccione para copiar %1$s para o portapapeis + A criar a chave da base de dados… + Base de dados + Díxitos + Accedido + Cancelar + Notas + Confirmar contrasinal + Criado + Caduca + Modificado + Contrasinal + Gardar + Nome + URL + Usuario + \ No newline at end of file From e3a07377e3b542f5333610c8b92c07f3c44789fd Mon Sep 17 00:00:00 2001 From: naofum Date: Thu, 17 Jan 2019 06:39:25 +0000 Subject: [PATCH 047/289] Translated using Weblate (Japanese) Currently translated at 31.3% (108 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/ --- app/src/main/res/values-ja/strings.xml | 59 ++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 014c47880..25da52fa6 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -16,8 +16,7 @@ You should have received a copy of the GNU General Public License along with KeePass DX. If not, see . ---> - +--> フィードバック: WEBサイト: \"KeePass DX\"はパスワードマネージャソフト\"KeePass\"をAndroid上で利用する為のアプリです。 @@ -36,9 +35,9 @@ クリップボード タイムアウト コピーした情報をクリップボードから消去する時間 ここをタップすると%1$sをクリップボードにコピーします - データベース認証中… + データベース認証中… データベース - データベースを解析中… + データベースを解析中… このデータベースを次回以降も利用する 数字 KeePass DX \u00A9 %1$d Kunzisoft によって作られたフリーソフトウェアであり、無保証です。GPLバージョン3以上の条件下でこれを再頒布することができます。 @@ -91,7 +90,7 @@ 生成するパスワードの長さ グループ一覧サイズ グループ一覧の文字サイズ - データベース読込中… + データベース読込中… 英数小文字 パスワードを隠す パスワード欄をアスタリスクで表示します @@ -115,8 +114,8 @@ 以前使用したデータベースを開く: 検索対象から除外 \"バックアップ\"と\"ごみ箱\"を検索対象から除外します - データベースファイルを作成中… - 実行中… + データベースファイルを作成中… + 実行中… 前回使用したキーファイルを次回も表示します キーファイルを記憶 消去 @@ -125,7 +124,7 @@ 暗号化レベル 暗号化レベルを高く設定するとブルートフォース(総当り)攻撃に強くなりますが、保存や読込に時間が掛かります。 レベル - データベースを保存中… + データベースを保存中… 半角スペース 検索 並べ替え @@ -156,4 +155,46 @@ - + エントリーを編集 + 文字列を追加 + 暗号化 + 鍵導出関数 + 今後、表示しない + 拡張 ASCII + 許可 + クリップボードエラー + 一部の Samsung Android 携帯電話はアプリからクリップボードが使用できません。 + クリップボードをクリアできません + スワイプすると今すぐクリップボードをクリアします + エントリデータが見つかりませんでした。 + データベースを読み込みできませんでした。 + 鍵を読み込みできませんでした。KDF の \"メモリ使用量\" を下げてみてください。 + 各文字列にフィールド名を指定してください。 + 自動入力サービスを有効にできませんでした。 + 自分自身のグループを移動することはできません。 + フィールド名 + フィールド値 + ファイルが見つかりません。ファイルブラウザーからもう一度開いてみてください。 + アルゴリズムが間違っています。 + 鍵ファイルが存在しません。 + 鍵ファイルが空です。 + ユーザー名を表示 + エントリーリストのユーザー名を表示します + %1$s のコピー + フォーム入力 + コピー + 移動 + 貼り付け + キャンセル + 保存された指紋を削除 + 書き込み保護 + 変更可能 + 新しいデータベースを作成 + 保護 + 書き込み保護 + Keepass DX は、データベースを変更するために書き込みアクセス許可が必要です。 + Android Kitkat から、一部のデバイスはアプリが SD カードに書き込むことができません。 + 最近使用したファイルの履歴 + 最近使用したファイル名を記憶します + すべてのデータで使用するデータベース暗号化アルゴリズム。 + \ No newline at end of file From 9864d1e62dccee6d82cc855168fbb364f6f42040 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 24 Jan 2019 18:21:44 +0100 Subject: [PATCH 048/289] Fix setting opening --- .../activities/lock/LockingActivity.kt | 24 ++++++++++++++----- .../keepass/settings/SettingsActivity.kt | 19 +++++---------- .../com/kunzisoft/keepass/utils/MenuUtil.kt | 4 ++-- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index 4ead0a44d..2a6d9fe90 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -41,6 +41,9 @@ abstract class LockingActivity : StylishActivity() { const val LOCK_ACTION = "com.kunzisoft.keepass.LOCK" const val RESULT_EXIT_LOCK = 1450 + + const val TIMEOUT_ENABLE_KEY = "TIMEOUT_ENABLE_KEY" + const val TIMEOUT_ENABLE_KEY_DEFAULT = true } protected var timeoutEnable: Boolean = true @@ -53,6 +56,14 @@ abstract class LockingActivity : StylishActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + if (savedInstanceState != null + && savedInstanceState.containsKey(TIMEOUT_ENABLE_KEY)) { + timeoutEnable = savedInstanceState.getBoolean(TIMEOUT_ENABLE_KEY) + } else { + if (intent != null) + timeoutEnable = intent.getBooleanExtra(TIMEOUT_ENABLE_KEY, TIMEOUT_ENABLE_KEY_DEFAULT) + } + if (timeoutEnable) { if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this)) { lockReceiver = LockReceiver() @@ -82,13 +93,13 @@ abstract class LockingActivity : StylishActivity() { override fun onResume() { super.onResume() - // End activity if database not loaded - if (!App.getDB().loaded) { - finish() - return - } - if (timeoutEnable) { + // End activity if database not loaded + if (!App.getDB().loaded) { + finish() + return + } + // After the first creation // or If simply swipe with another application // If the time is out -> close the Activity @@ -101,6 +112,7 @@ abstract class LockingActivity : StylishActivity() { override fun onSaveInstanceState(outState: Bundle) { ReadOnlyHelper.onSaveInstanceState(outState, readOnly) + outState.putBoolean(TIMEOUT_ENABLE_KEY, timeoutEnable) super.onSaveInstanceState(outState) } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 40d6148cc..5a27c5b31 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -26,11 +26,9 @@ import android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.widget.Toolbar import android.view.MenuItem - import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.ReadOnlyHelper import com.kunzisoft.keepass.activities.lock.LockingActivity -import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.timeout.TimeoutHelper @@ -44,18 +42,14 @@ open class SettingsActivity : LockingActivity(), MainPreferenceFragment.Callback private const val TAG_NESTED = "TAG_NESTED" - fun launch(activity: Activity, readOnly: Boolean) { + fun launch(activity: Activity, readOnly: Boolean, timeoutEnable: Boolean) { val intent = Intent(activity, SettingsActivity::class.java) ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly) - activity.startActivity(intent) - } - - fun launch(activity: Activity, readOnly: Boolean, checkLock: Boolean) { - // To avoid flickering when launch settings in a LockingActivity - if (!checkLock) - launch(activity, readOnly) - else if (TimeoutHelper.checkTime(activity)) { - launch(activity, readOnly) + intent.putExtra(TIMEOUT_ENABLE_KEY, timeoutEnable) + if (!timeoutEnable) { + activity.startActivity(intent) + } else if (TimeoutHelper.checkTime(activity)) { + activity.startActivity(intent) } } } @@ -69,7 +63,6 @@ open class SettingsActivity : LockingActivity(), MainPreferenceFragment.Callback } override fun onCreate(savedInstanceState: Bundle?) { - timeoutEnable = App.getDB().loaded super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt index e7378264c..854602b4e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt @@ -60,13 +60,13 @@ object MenuUtil { * @param checkLock Check the time lock before launch settings in LockingActivity */ @JvmOverloads - fun onDefaultMenuOptionsItemSelected(activity: StylishActivity, item: MenuItem, readOnly: Boolean = READ_ONLY_DEFAULT, checkLock: Boolean = false): Boolean { + fun onDefaultMenuOptionsItemSelected(activity: StylishActivity, item: MenuItem, readOnly: Boolean = READ_ONLY_DEFAULT, timeoutEnable: Boolean = false): Boolean { when (item.itemId) { R.id.menu_contribute -> return onContributionItemSelected(activity) R.id.menu_app_settings -> { // To avoid flickering when launch settings in a LockingActivity - SettingsActivity.launch(activity, readOnly, checkLock) + SettingsActivity.launch(activity, readOnly, timeoutEnable) return true } From 37b156653522e051c62286e86536ebf97516f8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Mesk=C3=B3?= Date: Wed, 6 Feb 2019 16:39:50 +0000 Subject: [PATCH 049/289] Translated using Weblate (Hungarian) Currently translated at 39.1% (135 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/ --- app/src/main/res/values-hu/strings.xml | 116 ++++++++++++++----------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 2ebdfd891..b3405e1ab 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -13,36 +13,35 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with KeePassDroid. If not, see . ---> - - Visszajelzés: - Weboldal: - KeePass DX a KeePass jelszókezelő android implementációja. +--> + Visszajelzés + Honlap + A KeePass jelszókezelő androidos megvalósítása Elfogadás Bejegyzés hozzáadása Csoport hozzáadása - Algoritmus + Titkosítási algoritmus Alkalmazás időkorlátja - Beállíthatja, mennyi idő után kerüljön lezárásra az adatbázis + Tétlenség az alkalmazás zárolása előtt Alkalmazás - Alkalmazás beállítások + Alkalmazásbeállítások "Ne mutassa többet" Zárójelek - A fájl kiválasztásához önnek egy nyílt forrású fájlkezelő programra lesz szüksége, kattintson a lenti telepítés linkre. Előfordulhat, hogy első használatakor nem fog megfelelően működni a fájlkezelő program. - Mégsem - Vágólap törölve. + Fájlok böngészése az OpenIntents fájlkezelő telepítésével + Mégse + Vágólap törölve Vágólap hiba - Hiba történt a vágólap használata során. A további részletekért tekintse meg az alábbi oldalt: - Hiba történt a vágólap törlésekor + Egyes androidos Samsung telefonok nem engedik, hogy az alkalmazások használják a vágólapot. + A vágólap törlése meghiúsult Vágólap időkorlátja - Beállíthatja, mennyi idő után kerüljön törlésre a vágólap + A vágólapon tárolás időtartama %1$s másolása a vágólapra Adatbázis létrehozása… Adatbázis Adatbázis tartalmának dekódolása… Adatbázis beállítása alapértelmezettként Számok - KeePass DX \u00A9 %1$d Kunzisoft. Ehhez a programhoz SEMMILYEN GARANCIA NEM JÁR; Ez egy szabad szoftver, GNU General Public License v3 vagy későbbi verziójának feltételei mellett terjeszthető, illetve módosítható. Fordította: Eversmann + KeePass DX © %1$d Kunzisoft. Ehhez a programhoz SEMMILYEN GARANCIA NEM JÁR; Ez egy szabad szoftver, a GNU General Public License 3-as vagy későbbi verziójának feltételei mellett terjeszthető, illetve módosítható. Adja meg az adatbázis fájlnevét Utolsó hozzáférés Mégsem @@ -57,56 +56,56 @@ Név URL Felhasználónév - Az ArcFour folyamrejtjel nem támogatott. - Nem található kezelő ehhez az uri-hoz. - Nem sikerült létrehozni szülő könyvtárat. - A fájl már létezik. - Hiba a cím megnyitásakor. - Fájlnév szükséges. + Az ARCFOUR adatfolyam-titkosítás nem támogatott. + Ez az URI nem kezelhető a KeePass DX-ben. + Nem sikerült létrehozni a szülőkönyvtárat. + Ez a fájl már létezik. + A hivatkozás nem nyitható meg. + Adjon meg egy fájlnevet. Nem sikerült létrehozni a fájlt: - Érvénytelen adatbázis. - Érvénytelen útvonal. - Név szükséges. - Jelszóra vagy kulcsfájlra van szükség. - A telefon memóriája megtelt az adatbázis feldolgozása közben. Lehet túl sok ez a telefonnak. - Legalább egy jelszógenerálási típust kell választania + Az adatbázist nem lehet olvasni. + Győződjön meg róla, hogy az útvonal helyes. + Adjon meg egy nevet. + Válasszon egy kulcsfájlt. + Nincs elég memória a teljes adatbázis betöltéséhez. + Legalább egy jelszóelőállítási típust kell választania. A jelszavak nem egyeznek meg. - A mező csak számokat tartalmazhat. - A menetek száma túl nagy. A maximális 2147483648. - Az érték mező kitöltése kötelező. - Név szükséges. - Írjon be egy pozitív egész számot a hossz mezőbe - Megnevezés - Érték - A fájl nem található - A fájl nem található. Próbálja meg újra megnyitni. + A „Transzformációs körök” szám kell legyen. + A „Transzformációs körök” száma túl nagy. Beállítás 2147483648-ra. + Minden karakterlánchoz szükséges egy mezőnév. + Adjon hozzá egy címet. + Írjon be egy pozitív egész számot a „Hossz” mezőbe. + Mezőnév + Mezőérték + A fájl nem található. + A fájl nem található. Próbálja meg újra megnyitni a fájlkezelőben. Fájlkezelő - Jelszó generálás + Jelszó előállítása jelszó megerősítése - generált jelszó + előállított jelszó Csoportnév kulcsfájl hosszúság jelszó Jelszó - Letöltés a Google Play-ről - Letöltés a F-Droid - Érvénytelen jelszó vagy kulcsfájl. - Érvénytelen algoritmus. - Az adabázis formátuma nem ismert. - A kulcsfájl nem létezik. + Telepítés a Play Áruházból + Telepítés az F-Droidból + A jelszó vagy kulcsfájl nem olvasható. + Hibás algoritmus. + Az adatbázis formátuma nem ismerhető fel. + Nem létezik kulcsfájl. A kulcsfájl üres. Hossz - Csoportok betűmérete - Beállítja a csoportok betűméretét + Listaelemek mérete + Szövegméret az elemlistában Adatbázis betöltése… Kisbetűk Jelszó elrejtése - Jelszavak elrejtése alapértelmezésben + Jelszavak alapértelmezett elrejtése (***) Névjegy - Mesterkulcs megváltoztatása + Mesterkulcs cseréje Beállítások - Adatbázis beállítások + Adatbázis-beállítások Törlés Támogatás Szerkesztés @@ -182,4 +181,23 @@ Közepes Nagy + Bejegyzés szerkesztése + Karakterlánc hozzáadása + Titkosítás + Kulcsváltozási függvény + Bővített ASCII + Engedélyezés + A bejegyzésadatok nem találhatóak. + Az adatbázis betöltése meghiúsult. + A kulcs nem tölthető be. Próbálja meg csökkenteni a KDF „Memóriahasználatot”. + Az automatikus kitöltési szolgáltatás nem engedélyezhető. + Nem helyezheti át a csoportot saját magába. + Felhasználónevek megjelenítése + Felhasználónevek megjelenítése a bejegyzéslistákban + %1$s másolata + Űrlapkitöltés + Másolás + Áthelyezés + Beillesztés + Mégse \ No newline at end of file From 337816b7f2b470c48c75423cdc093b7f76bfaac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Mesk=C3=B3?= Date: Thu, 7 Feb 2019 09:17:18 +0000 Subject: [PATCH 050/289] Translated using Weblate (Hungarian) Currently translated at 92.2% (318 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/ --- app/src/main/res/values-hu/strings.xml | 235 +++++++++++++++++++++---- 1 file changed, 197 insertions(+), 38 deletions(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index b3405e1ab..a784aefdf 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -38,14 +38,14 @@ %1$s másolása a vágólapra Adatbázis létrehozása… Adatbázis - Adatbázis tartalmának dekódolása… - Adatbázis beállítása alapértelmezettként + Adatbázis-tartalom titkosításának feloldása… + Használat alapértelmezett adatbázisként Számok KeePass DX © %1$d Kunzisoft. Ehhez a programhoz SEMMILYEN GARANCIA NEM JÁR; Ez egy szabad szoftver, a GNU General Public License 3-as vagy későbbi verziójának feltételei mellett terjeszthető, illetve módosítható. - Adja meg az adatbázis fájlnevét + Létező adatbázis megnyitása Utolsó hozzáférés Mégsem - Megjegyzés + Megjegyzések Jelszó megerősítése Létrehozva Lejárat @@ -100,7 +100,7 @@ Szövegméret az elemlistában Adatbázis betöltése… Kisbetűk - Jelszó elrejtése + Jelszavak elrejtése Jelszavak alapértelmezett elrejtése (***) Névjegy Mesterkulcs cseréje @@ -110,7 +110,7 @@ Támogatás Szerkesztés Jelszó elrejtése - Adatbázis lezárása + Adatbázis zárolása Megnyitás Keresés Jelszó megjelenítése @@ -118,52 +118,54 @@ Mínusz Soha Nincs találat - Nem található kezelő ehhez az url-hez. - Korábbi adatbázis megnyitása : - Keresési kivételek - A Backup és Lomtár csoportok kihagyása a keresésből + Telepítsen egy webböngészőt az URL megnyitásához. + Korábbi adatbázisok + Ne keressen a biztonsági mentésekben + A „Biztonsági mentés” csoport kihagyása a keresésből (csak a .kdb fájlokra érvényes) Új adatbázis létrehozása… Feldolgozás… - Memórián belüli védelem - Csak olvasható - A programnak nincs engedélye az adatbázis írásához a jelenlegi helyén, ezért a megnyitása után csak olvasható lesz. - A KitKat-es android verziótól kezdődően a futó alkalmazásoknak nincs jogosultságuk írni a külső SD kártyára. - Adatbázis mentése - Jegyezze meg az adatbázisok helyét - Jegyezze meg a kulcsfájlok helyét + Védelem + Írásvédett + A KeePass DX-nek írási engedélyre van szüksége, hogy bármit is módosíthasson az adatbázisban. + Az Android KitKattől kezdődően, egyes eszközökön az alkalmazások nem írhatnak az SD kártyára. + Előző fájlok előzményei + Az előző fájlnevek megjegyzése + Megjegyzi az adatbázis-kulcsfájlok helyét Kulcsfájl mentése Eltávolítás Rijndael (AES) Gyökérkönyvár - Titkosítási menetek száma - A titkosítási menetek számának növelésével extra védelemet kaphat a brute force támadások ellen, ugyanakkor jelentősen lassíthatja az adatbázis betöltését vagy mentését. - Menetek száma + Transzformációs körök + A további titkosítási körök magasabb védelmet biztosítanak a nyers erőt használó támadások ellen, ugyanakkor jelentősen lassíthatják az adatbázis betöltését vagy mentését. + transzformációs körök Adatbázis mentése… Szóköz Keresés - Rendezés dátum alapján + Természetes adatbázis Speciális - Bejegyzés név/leírás - Keresés eredménye + Keresés + Találatok Twofish Aláhúzás - Nem támogatott adatbázis. - Nagybetűk - Használja az Android Storage Access Framework-öt a fájlok böngészéséhez (KitKat vagy későbbi) - Storage Access Framework + Nem támogatott adatbázis-verzió. + Nagybetűs + Az androidos tároló-hozzáférési keretrendszer (SAF) használata a fájlok böngészéséhez (KitKattől kezdve) + Tároló-hozzáférési keretrendszer Figyelmeztetés - A .kdb formátum csak a Latin1 karakterkészletet támogatja. A jelenlegi jelszó olyan karaktereket is tartalmazhat, amik ezen kívül esnek. Az összes ilyen karakter át lesz konvertálva, viszont ez nagyban csökkenteni fogja a jelszó erősségét. Mielőbbi megváltoztatása ajánlott. - A memóriakártya jelenleg el van távolítva. Nem fogja tudni az adatbázist betölteni vagy módosítani. - Verzió %1$s - Az ujjlenyomat használat nincs még beállítva az eszközön - Várakozás az ujjelnyomat megadására - Titkosított jelszó elmentve - Érvénytelen kulcs. Helyezze vissza jelszavát. - Érvénytelen ujjlenyomat : %1$s - Használjon ujjlenyomatot a jelszó elmentéséhez - Nincs még jelszó beállítva ehhez az adatbázishoz + Kerülje a Latin-1 karakterkészlettől eltérő jelszókaraktereket a .kbd fájlokban, mert ugyanarra a betűre lesznek átalakítva. + Csatolja az SD kártyát, hogy adatbázist hozzon létre vagy töltsön be. + Verzió: %1$s + Az ujjlenyomat-leolvasás támogatott, de nincs beállítva. + Ujjlenyomat-leolvasás + Titkosított jelszó tárolva + Az ujjlenyomat kulcs nem olvasható. Állítsa vissza a jelszavát. + Ujjlenyomat probléma: %1$s + Használjon ujjlenyomatot a jelszó tárolásához + Az adatbázisnak még nincs jelszava. - Adja meg a jelszót és/vagy a kulcsfájlt, hogy kinyithassa az adatbázist. + Adja meg a jelszót és/vagy a kulcsfájlt, hogy kinyithassa az adatbázist. +\n +\nKészítsen biztonsági mentést az adatbázisról minden egyes módosítás után. 5 másodperc @@ -200,4 +202,161 @@ Áthelyezés Beillesztés Mégse + Mentett ujjlenyomat törlése + Írásvédett + Módosítható + Új adatbázis létrehozása + Az összes adathoz használt adatbázis-titkosítási algoritmus. + A kulcs előállításához a titkosítási algoritmushoz, a mesterkulcs átalakításra került egy véletlenszerűen sózott kulcselőállítási függvénnyel. + Memóriahasználat + A kulcselőállítási függvényhez használt memóriamennyiség (bináris bájtokban). + Párhuzamosság + A kulcselőállítási függvény párhuzamosságának mértéke (azaz a szálak száma). + Rendezés + Legalacsonyabb elöl ↓ + Csoportok elöl + Kuka alul + Cím + Felhasználónév + Létrehozás + Módosítás + Hozzáférés + SD kártya írási jogosultság megadása az adatbázis-változások mentéséhez. + Biztos, hogy nem akar jelszavas feloldási védelmet\? + Biztos, hogy nem akar semmilyen titkosítási kulcsot használni\? + Összeállítás: %1$s + Az ujjlenyomat nem ismerhető fel + Előzmények + Megjelenés + Általános + Automatikus kitöltés + KeePass DX űrlapkitöltés + Bejelentkezés a KeePass DX-szel + Alapértelmezett automatikus kitöltési szolgáltatás beállítása + Automatikus kitöltés engedélyezése az űrlapok gyors kitöltéséhez más alkalmazásokban + Előállított jelszó mérete + Beállítja az előállított jelszavak alapértelmezett méretét + Jelszó karakterek + Beállítja a jelszó-előállító számára megengedett karaktereket + Vágólap + Vágólap-értesítések + A vágólap-értesítések engedélyezése a bejegyzésmezők másolásához + Ha a vágólap automatikus törlése meghiúsul, akkor törölje kézzel az előzményeket. + Zárolás + Képernyőzár + Az adatbázis zárolása, ha a képernyő kikapcsol + Hogyan állítsa be az ujjlenyomat-olvasást a gyors feloldáshoz\? + Mentse a leolvasott ujjlenyomatát az eszközén a + „Beállítások” → „Biztonság” → „Ujjlenyomat” menüpontban + Adja meg az adatbázis zárolási jelszavát + Olvassa le az ujjlenyomatát, hogy biztonságosan tárolja az adatbázist zároló jelszót. + Olvassa le az ujjlenyomatát, hogy kinyissa az adatbázist, ha a jelszó ki van kapcsolva. + Használat + Ujjlenyomat + Ujjlenyomat-leolvasás + Lehetővé teszi, hogy leolvassa az ujjlenyomatát az adatbázis feloldásához + Titkosítási kulcsok törlése + Az összes ujjlenyomat-felismeréssel kapcsolatos titkosítási kulcs törlése + Biztos, hogy törli az összes ujjlenyomat-felismeréssel kapcsolatos titkosítási kulcsot\? + A funkciót nem sikerült elindítani. + Az Android verziója (%1$s) nem éri el a minimálisan szükséges verziót (%2$s). + Nem található a megfelelő hardver. + Fájlnév + Útvonal + Mesterkulcs hozzárendelése + Bájtok + Fájlútvonal + A teljes fájlútvonal megtekintése + Kuka használata + A csoportok és bejegyzések „Kukába” helyezése törlés előtt + A KeePass DX-nek külső tároló jogosultságra van szüksége az adatbázis írásához. + A KeePass DX-nek külső tároló jogosultságra van szükséges a nem tartalomszolgáltató által biztosított URI olvasásához. + Nem sikerült külső tároló jogosultságot szerezni. + A művelet nem hajtható végre külső tároló jogosultság nélkül. + Mező betűkészlete + A mezőkben használt betűkészlet módosítása a karakterek jobb láthatósága érdekében + Fájlok megnyitása kiválasztással + Fájlok automatikus megnyitása, ha kiválasztásra kerülnek a fájlböngészőben + Megbízás a vágólapban + Engedélyezés, hogy a jelszó és a védett mezők a vágólapra kerüljenek + FIGYELMEZTETÉS: A vágólapon osztozik az összes alkalmazás. Ha érzékeny adatokat másol, akkor a többi szoftver is hozzáférhet. + FIGYELMEZTETÉS: A funkció kikapcsolása megakadályozhatja az adatbázisok megnyitását vagy mentését. + A megnyitandó adatbázisfájl hivatkozása + Adatbázis neve + Adatbázis leírása + Adatbázis verzió + Szöveg + Alkalmazás + Egyéb + Billentyűzet + Mágikus billentyűzet + Egyéni billentyűzet aktiválása, amely kitölti a jelszavakat és az összes azonosító mezőt + Mágikus billentyűzet beállításai + Billentyűzet beállítása az űrlapok biztonságos automatikus kitöltéséhez. + Aktiválja a „Mágikus billentyűzetet” az eszközbeállításokban. + „Beállítások” → „Nyelvek és bevitel” → „Jelenlegi billentyűzet” és válasszon egyet. + vagy („Beállítások” → „Nyelv és bevitel” → „Virtuális billentyűzet” és válasszon egyet.) + Válassza a Mágikus billentyűzetet, ha egy űrlapot kell kitöltenie. + Válasszon a a billentyűzetek közt a billentyűzet szóközének hosszú nyomásával, vagy ha nem érhető el, akkor így: + Válassza ki a bejegyzést a gombbal. + Töltse ki a mezőket a bejegyzés elemeivel. + Zárolja az adatbázist. + Használja újra az alapértelmezett billentyűzetet. + Mágikus billentyűzet + Mágikus billentyűzet (KeePass DX) + Mágikus billentyűzet beállításai + Bejegyzés + Időtúllépés + Időtúllépés a billentyűzet bejegyzés törléséhez + Értesítési információ + Értesítés megjelenítése, ha egy bejegyzés elérhető + Bejegyzés + %1$s elérhető a Mágikus billentyűzeten + %1$s + Törlés bezáráskor + A billentyűzet bejegyzés törlése az értesítés bezárásakor + Megjelenés + Billentyűzet téma + Kulcsok + Rezgés gombnyomáskor + Hang gombnyomáskor + Üres jelszó engedélyezése + A „Megnyitás” gomb engedélyezése, ha nem volt kiválasztva jelszavas azonosítás + Írásvédett + Az adatbázis megnyitása alapértelmezetten csak olvasható módban + Oktató képernyők + Elemek kiemelése, hogy megtudja hogyan működik az alkalmazás + Oktató képernyők visszaállítása + Az összes oktatási elem újbóli megjelenítése + Oktató képernyők visszaállítása + Saját adatbázisfájl létrehozása + Hozza létre az első jelszókezelő-fájlját. + Létező adatbázis megnyitása + Nyisson meg egy korábbi adatbázisfájlt a fájlböngészőből, hogy folytassa a használatát. + Egy a fájl helyére mutató hivatkozás is megfelelő + Egy fizikai hivatkozással is megnyithatja az adatbázist (például egy file:// vagy content:// hivatkozással). + Elemek hozzáadása az adatbázishoz + A bejegyzések segítenek a digitális személyazonosságai kezelésében. +\n +\nA csoportok (~ mappák) rendszerezik a bejegyzéseket az adatbázisban. + Keresés a bejegyzések között + Adja meg a címet, felhasználónevet vagy más mezők tartalmát, hogy lekérje a jelszavát. + Adatbázis feloldása ujjlenyomattal + Kösse össze a jelszavát a mentett ujjlenyomatával, hogy gyorsan fel tudja oldani az adatbázist. + Bejegyzés szerkesztése + Hozzon létre erős jelszót a bejegyzéshez. + Állítson elő egy erős jelszót, és rendelje hozzá a bejegyzéshez, adja meg egyszerűen az űrlap feltételeinek megfelelően, és ne felejtse el biztonságosan tárolni. + Egyéni mezők hozzáadása + Regisztráljon újként egy egyszerű nem kitöltendő mezőt, így azt is megvédheti. + Az adatbázis feloldása + Az adatbázis írásvédelme + Módosítsa a megnyitás módját a munkamenetben. +\n +\nAz „Írásvédett” mód megakadályozza az adatbázis nem szándékos módosítását. +\n„Módosítható” módban új elemeket adhat hozzá, törölhet, és módosíthatja az összes elemet. + Mező másolása + A másolt mezők bárhová beilleszthetőek. +\n +\nHasználja a kedvenc mezőkitöltési módját. + Az adatbázis zárolása \ No newline at end of file From 76ed603bf16b9ddc839adb83e006d516e9fba7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Mesk=C3=B3?= Date: Mon, 11 Feb 2019 09:01:31 +0000 Subject: [PATCH 051/289] Translated using Weblate (Hungarian) Currently translated at 93.3% (322 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/ --- app/src/main/res/values-hu/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index a784aefdf..b24945486 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -359,4 +359,8 @@ \n \nHasználja a kedvenc mezőkitöltési módját. Az adatbázis zárolása + Zárolja gyorsan az adatbázist, beállíthatja hogy az alkalmazás lezárja egy idő után, valamint ha a képernyőt kikapcsolja. + Elemek rendezése + Válasszon, hogy az elemek és csoportok hogyan legyenek rendezve. + Közreműködés \ No newline at end of file From b7f9ff8c9878c1dbf26bfd8b072eabd907b6118b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Mesk=C3=B3?= Date: Mon, 11 Feb 2019 13:31:41 +0000 Subject: [PATCH 052/289] Translated using Weblate (Hungarian) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hu/ --- app/src/main/res/values-hu/strings.xml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index b24945486..06b3824f1 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -196,7 +196,7 @@ Nem helyezheti át a csoportot saját magába. Felhasználónevek megjelenítése Felhasználónevek megjelenítése a bejegyzéslistákban - %1$s másolata + %1$s másolva Űrlapkitöltés Másolás Áthelyezés @@ -363,4 +363,27 @@ Elemek rendezése Válasszon, hogy az elemek és csoportok hogyan legyenek rendezve. Közreműködés + Seprés a vágólap azonnali törléséhez + Szerkessze a bejegyzése egyéni mezőit. Az adatok hivatkozhatóak a különböző mezők között. + Segítsen a stabilitás és a biztonság növelésében, és az új funkciók hozzáadásában. + Számos más jelszókezelő alkalmazással ellentétben, ez egy reklámmentes, copyleft licencelésű szabad szoftver, amely nem gyűjt személyes adatokat a kiszolgálókon, bármelyik verziót is használja. + A pro verzió megvásárlásával hozzáférést kap ehhez a vizuális funkcióhoz, és segít a közösségi projektek megvalósulásában. + Ez a vizuális funkció az Ön nagylelkűségének köszönhetően érhető el. + Ahhoz, hogy megtartsuk a szabadságunkat, és hogy mindig aktívak lehessünk, számítunk az Ön támogatására. + Ez a funkció fejlesztés alatt áll, és az Ön támogatására van szükség, hogy hamarosan elérhető legyen. + A pro verzió megvásárlásával, + A támogatással + arra ösztönzi a fejlesztőket, hogy új funkciókat készítsenek, és hibákat javítsanak az észrevételei alapján. + Köszönjük a támogatását. + Keményen dolgozunk, hogy gyorsan kiadjuk ezt a funkciót. + Ne felejtse naprakészen tartani az alkalmazást az új verziók telepítésével. + Letöltés + Támogatás + ChaCha20 + AES KDF + Argon2 + Alkalmazástéma + Az alkalmazásban használt téma + Ikoncsomag + Az alkalmazásban használt ikoncsomag \ No newline at end of file From 5e901812ad51242540c0bf74e7a485854e83507e Mon Sep 17 00:00:00 2001 From: naofum Date: Sat, 16 Feb 2019 08:23:59 +0000 Subject: [PATCH 053/289] Translated using Weblate (Japanese) Currently translated at 32.8% (113 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/ --- app/src/main/res/values-ja/strings.xml | 36 ++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 25da52fa6..efce818bd 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -17,14 +17,14 @@ You should have received a copy of the GNU General Public License along with KeePass DX. If not, see . --> - フィードバック: - WEBサイト: - \"KeePass DX\"はパスワードマネージャソフト\"KeePass\"をAndroid上で利用する為のアプリです。 + フィードバック + ホームページ + KeePass パスワードマネージャーの Android 実装 決定 エントリーを追加 グループを追加 - アルゴリズム - アプリケーション タイムアウト + 暗号化アルゴリズム + アプリ タイムアウト KeePassがこの時間非アクティブだった場合、データベースをロックします。 アプリケーション アプリケーション設定 @@ -136,24 +136,22 @@ 英数大文字 SDカードがマウントされていません。データベースの読込や作成ができません。 バージョン %1$s - データベースに設定したパスワードを入力するかキーファイルを選択してください。 - - 5秒 - 10秒 - 20秒 - 30秒 - 1分 - 5分 - 15分 - 30分 - なし + 5秒 + 10秒 + 20秒 + 30秒 + 1分 + 5分 + 15分 + 30分 + なし - - - + + + エントリーを編集 文字列を追加 From e0b0dd134f80978ea316914addde41bea7341e04 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 21 Feb 2019 13:50:07 +0100 Subject: [PATCH 054/289] Refactor launch method --- .../keepass/activities/GroupActivity.java | 43 ++++-- .../activities/IntentBuildLauncher.java | 2 +- .../keepass/password/PasswordActivity.java | 142 ++++++++++-------- 3 files changed, 110 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index a3c61ccf1..9061e63fe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -139,6 +139,24 @@ public class GroupActivity extends LockingActivity private int iconColor; + private static void buildAndLaunchIntent(Activity activity, PwGroup group, boolean readOnly, + IntentBuildLauncher intentBuildLauncher) { + if (TimeoutHelper.INSTANCE.checkTime(activity)) { + Intent intent = new Intent(activity, GroupActivity.class); + if (group != null) { + intent.putExtra(GROUP_ID_KEY, group.getId()); + } + ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly); + intentBuildLauncher.launchActivity(intent); + } + } + + /* + * ------------------------- + * Standard Launch + * ------------------------- + */ + // After a database creation public static void launch(Activity act) { launch(act, READ_ONLY_DEFAULT); @@ -149,23 +167,18 @@ public class GroupActivity extends LockingActivity launch(act, null, readOnly); } - private static void buildAndLaunchIntent(Activity activity, PwGroup group, boolean readOnly, - IntentBuildLauncher intentBuildLauncher) { - if (TimeoutHelper.INSTANCE.checkTime(activity)) { - Intent intent = new Intent(activity, GroupActivity.class); - if (group != null) { - intent.putExtra(GROUP_ID_KEY, group.getId()); - } - ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly); - intentBuildLauncher.startActivityForResult(intent); - } - } - public static void launch(Activity activity, PwGroup group, boolean readOnly) { buildAndLaunchIntent(activity, group, readOnly, (intent) -> activity.startActivityForResult(intent, 0)); } + + /* + * ------------------------- + * Keyboard Launch + * ------------------------- + */ + public static void launchForKeyboardResult(Activity activity, boolean readOnly) { TimeoutHelper.INSTANCE.recordTime(activity); launchForKeyboardResult(activity, null, readOnly); @@ -179,6 +192,12 @@ public class GroupActivity extends LockingActivity }); } + /* + * ------------------------- + * Autofill Launch + * ------------------------- + */ + @RequiresApi(api = Build.VERSION_CODES.O) public static void launchForAutofillResult(Activity activity, AssistStructure assistStructure, boolean readOnly) { if ( assistStructure != null ) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IntentBuildLauncher.java b/app/src/main/java/com/kunzisoft/keepass/activities/IntentBuildLauncher.java index f7eddb8be..0d3e0e4be 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IntentBuildLauncher.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IntentBuildLauncher.java @@ -3,5 +3,5 @@ package com.kunzisoft.keepass.activities; import android.content.Intent; public interface IntentBuildLauncher { - void startActivityForResult(Intent intent); + void launchActivity(Intent intent); } diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index c6420158b..64fcfea40 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -58,6 +58,7 @@ import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.GroupActivity; import com.kunzisoft.keepass.activities.IntentBuildLauncher; import com.kunzisoft.keepass.activities.ReadOnlyHelper; +import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.compat.ClipDataCompat; @@ -69,7 +70,6 @@ import com.kunzisoft.keepass.fileselect.KeyFileHelper; import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; -import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.selection.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; @@ -136,6 +136,37 @@ public class PasswordActivity extends StylishActivity protected boolean entrySelectionMode; private AutofillHelper autofillHelper; + private static void buildAndLaunchIntent(Activity activity, String fileName, String keyFile, + IntentBuildLauncher intentBuildLauncher) { + Intent intent = new Intent(activity, PasswordActivity.class); + intent.putExtra(UriIntentInitTask.KEY_FILENAME, fileName); + intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile); + intentBuildLauncher.launchActivity(intent); + } + + private static void verifyFileNameUriFromLaunch(String fileName) throws FileNotFoundException { + if (EmptyUtils.isNullOrEmpty(fileName)) { + throw new FileNotFoundException(); + } + + Uri uri = UriUtil.parseDefaultFile(fileName); + assert uri != null; + String scheme = uri.getScheme(); + + if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { + File dbFile = new File(uri.getPath()); + if (!dbFile.exists()) { + throw new FileNotFoundException(); + } + } + } + + /* + * ------------------------- + * Standard Launch + * ------------------------- + */ + public static void launch( Activity act, String fileName) throws FileNotFoundException { @@ -147,20 +178,14 @@ public class PasswordActivity extends StylishActivity String fileName, String keyFile) throws FileNotFoundException { verifyFileNameUriFromLaunch(fileName); - - buildAndLaunchIntent(activity, fileName, keyFile, (intent) -> { - // only to avoid visible flickering when redirecting - activity.startActivityForResult(intent, RESULT_CANCELED); - }); + buildAndLaunchIntent(activity, fileName, keyFile, activity::startActivity); } - private static void buildAndLaunchIntent(Activity activity, String fileName, String keyFile, - IntentBuildLauncher intentBuildLauncher) { - Intent intent = new Intent(activity, PasswordActivity.class); - intent.putExtra(UriIntentInitTask.KEY_FILENAME, fileName); - intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile); - intentBuildLauncher.startActivityForResult(intent); - } + /* + * ------------------------- + * Keyboard Launch + * ------------------------- + */ public static void launchForKeyboardResult( Activity act, @@ -181,6 +206,12 @@ public class PasswordActivity extends StylishActivity }); } + /* + * ------------------------- + * Autofill Launch + * ------------------------- + */ + @RequiresApi(api = Build.VERSION_CODES.O) public static void launchForAutofillResult( Activity act, @@ -207,57 +238,6 @@ public class PasswordActivity extends StylishActivity } } - private static void verifyFileNameUriFromLaunch(String fileName) throws FileNotFoundException { - if (EmptyUtils.isNullOrEmpty(fileName)) { - throw new FileNotFoundException(); - } - - Uri uri = UriUtil.parseDefaultFile(fileName); - assert uri != null; - String scheme = uri.getScheme(); - - if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { - File dbFile = new File(uri.getPath()); - if (!dbFile.exists()) { - throw new FileNotFoundException(); - } - } - } - - @Override - protected void onActivityResult( - int requestCode, - int resultCode, - Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - // To get entry in result - EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } - - boolean keyFileResult = false; - if (keyFileHelper != null) { - keyFileResult = keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, - uri -> { - if (uri != null) { - populateKeyFileTextView(uri.toString()); - } - }); - } - if (!keyFileResult) { - // this block if not a key file response - switch (resultCode) { - case LockingActivity.RESULT_EXIT_LOCK: - case Activity.RESULT_CANCELED: - setEmptyViews(); - App.getDB().clear(getApplicationContext()); - break; - } - } - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -1072,6 +1052,40 @@ public class PasswordActivity extends StylishActivity PasswordActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); } + @Override + protected void onActivityResult( + int requestCode, + int resultCode, + Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + // To get entry in result + EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + } + + boolean keyFileResult = false; + if (keyFileHelper != null) { + keyFileResult = keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, + uri -> { + if (uri != null) { + populateKeyFileTextView(uri.toString()); + } + }); + } + if (!keyFileResult) { + // this block if not a key file response + switch (resultCode) { + case LockingActivity.RESULT_EXIT_LOCK: + case Activity.RESULT_CANCELED: + setEmptyViews(); + App.getDB().clear(getApplicationContext()); + break; + } + } + } + private static class UriIntentInitTask extends AsyncTask { static final String KEY_FILENAME = "fileName"; From d000b6f88492e287e59e3bba2f496b2b3149dca4 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 21 Feb 2019 14:17:26 +0100 Subject: [PATCH 055/289] Add lock button always visible --- .../com/kunzisoft/keepass/activities/EntryEditActivity.java | 5 +++++ app/src/main/res/menu/database_lock.xml | 2 +- app/src/main/res/menu/entry.xml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 06b52d4c2..c9641526e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -467,6 +467,7 @@ public class EntryEditActivity extends LockingHideActivity super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.database_lock, menu); MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); return true; @@ -474,6 +475,10 @@ public class EntryEditActivity extends LockingHideActivity public boolean onOptionsItemSelected(MenuItem item) { switch ( item.getItemId() ) { + case R.id.menu_lock: + lockAndExit(); + return true; + case R.id.menu_contribute: return MenuUtil.INSTANCE.onContributionItemSelected(this); diff --git a/app/src/main/res/menu/database_lock.xml b/app/src/main/res/menu/database_lock.xml index eb3a59068..387436df3 100644 --- a/app/src/main/res/menu/database_lock.xml +++ b/app/src/main/res/menu/database_lock.xml @@ -23,5 +23,5 @@ android:icon="@drawable/ic_lock_white_24dp" android:title="@string/menu_lock" android:orderInCategory="81" - app:showAsAction="ifRoom" /> + app:showAsAction="always" /> \ No newline at end of file diff --git a/app/src/main/res/menu/entry.xml b/app/src/main/res/menu/entry.xml index b768c098b..1c351e93d 100644 --- a/app/src/main/res/menu/entry.xml +++ b/app/src/main/res/menu/entry.xml @@ -28,7 +28,7 @@ android:icon="@drawable/ic_mode_edit_white_24dp" android:title="@string/menu_edit" android:orderInCategory="22" - app:showAsAction="always" /> + app:showAsAction="ifRoom" /> Date: Thu, 21 Feb 2019 14:20:35 +0100 Subject: [PATCH 056/289] Update version and Changelog --- CHANGELOG | 5 +++++ app/build.gradle | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c78ae7a32..7618fce57 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +KeepassDX (2.5.0.0beta19) + * Add lock button always visible + * Refactor connection workflow + * Better Magikeyboard connection + KeepassDX (2.5.0.0beta18) * New recent databases views * New information dialog diff --git a/app/build.gradle b/app/build.gradle index 25ef83ac2..10482c38c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 14 targetSdkVersion 27 - versionCode = 18 - versionName = "2.5.0.0beta18" + versionCode = 19 + versionName = "2.5.0.0beta19" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" From 9233b610c220c7621de009198f66af07aedf0824 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 21 Feb 2019 15:52:00 +0100 Subject: [PATCH 057/289] Refactor keys of preferences --- app/src/main/res/values/donottranslate.xml | 243 +++++++++++------- app/src/main/res/values/strings.xml | 10 - .../main/res/xml/appearance_preferences.xml | 6 +- .../main/res/xml/application_preferences.xml | 32 +-- .../main/res/xml/form_filling_preferences.xml | 4 +- 5 files changed, 165 insertions(+), 130 deletions(-) diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 54db9af15..a87461203 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -1,6 +1,6 @@ + + + KeePass DX KeePass DX - https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro http://code.google.com/p/android/issues/detail?id=35732 https://www.keepassdx.com/contribution @@ -35,121 +41,167 @@ https://play.google.com/store/apps/details?id=com.kunzisoft.keyboard.switcher https://f-droid.org/en/packages/com.kunzisoft.keyboard.switcher/ Keyboard Switcher - --,--`--,{@ - - password_length_key - 1 - 14 - 64 + + I + II + III + IV + V - + + /keepass/ + keepass + .kdbx + + @string/database_file_extension_default + + + + + + app + + allow_no_password_key + true + enable_read_only_key + false + omitbackup + true + app_timeout_key + lock_database_screen_off_key + true + password_length_key + list_password_generator_options_key + maskpass + true + allow_copy_password_key + false + recentfile + true + keyfile + true + storage_access_framework_key + false + auto_open_file_uri_key + true + fingerprint_enable_key + false + fingerprint_delete_all_key + + + settings_form_filling_key + + magic_keyboard_key + false + magic_keyboard_preference_key + clipboard_notifications_key + false + clip_timeout_key + 60000 + settings_autofill_enable_key + false + notification_entry_key + true + notification_entry_slide_close_key + true + erase_entry_timeout_key + keyboard_theme_key + keyboard_key_vibrate_key + true + key_sound_key + false + + + settings_appearance_key + + setting_style_key + setting_icon_pack_choose_key + list_entries_show_username_key + true + list_size + monospace_font_extra_fields_enable_key + true + full_file_path_enable_key + false + enable_education_screens_key + true + relaunch_education_screens_key + + + db + database_general_key + + database_name_key + database_description_key + recycle_bin_key + database_version_key algorithm key_derivation_function_key - app - timeout_backup_key - app_timeout_key - clip_timeout_key - db transform_rounds_key memory_usage_key parallelism_key - keyfile - maskpass - omitbackup - recentfile - list_entries_show_username_key - list_size + + + show_beta_warning show_read_only_warning + + sort_node_key sort_group_before_key - sort_ascending_key - sort_recycle_bin_bottom_key - storage_access_framework_key - setting_style_key - setting_icon_pack_choose_key - clipboard_notifications_key - lock_database_screen_off_key - fingerprint_enable_key - settings_form_filling_key - settings_autofill_enable_key - full_file_path_enable_key - fingerprint_delete_all_key - recycle_bin_key - monospace_font_extra_fields_enable_key - auto_open_file_uri_key - allow_copy_password_key - allow_copy_password_first_time_key - database_general_key - database_name_key - database_description_key - database_version_key - - enable_education_screens_key - relaunch_education_screens_key - education_create_db_key - education_select_db_key - education_open_link_db_key - education_unlock_key - education_read_only_key - education_search_key - education_new_node_key - education_sort_key - education_lock_key - education_copy_username_key - education_entry_edit_key - education_password_generator_key - education_entry_new_field_key - education_screen_reclicked_key - - settings_appearance_key - magic_keyboard_key - magic_keyboard_preference_key - allow_no_password_key - enable_read_only_key - - true - true - true - true - false - false - true - false - false - false true + sort_ascending_key true + sort_recycle_bin_bottom_key true - true - true - true - false + + allow_copy_password_first_time_key true - true + + + education_create_db_key false + education_select_db_key false + education_open_link_db_key false + education_unlock_key false + education_read_only_key false + education_search_key false + education_new_node_key false + education_sort_key false + education_lock_key false + education_copy_username_key false + education_entry_edit_key false + education_password_generator_key false + education_entry_new_field_key false + education_screen_reclicked_key false - false - true - false + + + timeout_backup_key 300000 - 60000 - 5000 10000 @@ -162,6 +214,7 @@ -1 + 32dp 13 16 @@ -171,6 +224,7 @@ 22 + KeepassDXStyle_Light KeepassDXStyle_Night KeepassDXStyle_Dark @@ -188,7 +242,11 @@ @string/material_resource_id - list_password_generator_options_key + + 1 + 14 + 64 + value_password_uppercase value_password_lowercase value_password_digits @@ -235,17 +293,4 @@ @string/extended_ASCII - I - II - III - IV - V - - /keepass/ - keepass - .kdbx - - @string/database_file_extension_default - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b72c2ee0..7d4d44cce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -298,37 +298,27 @@ Entry - erase_entry_timeout_key Timeout Timeout to clear the keyboard entry - notification_entry_key Notification information Show a notification when an entry is available - true Entry %1$s available on Magikeyboard %1$s - notification_entry_slide_close_key Clear at closing Clear the keyboard entry when closing the notification - true Appearance - keyboard_theme_key Keyboard theme Keys - keyboard_key_vibrate_key Vibrate on keypress - true - key_sound_key Sound on keypress - false Allow no password Enable the \"Open\" button if no password identification is selected diff --git a/app/src/main/res/xml/appearance_preferences.xml b/app/src/main/res/xml/appearance_preferences.xml index 316c33018..3ff2c57cb 100644 --- a/app/src/main/res/xml/appearance_preferences.xml +++ b/app/src/main/res/xml/appearance_preferences.xml @@ -23,16 +23,16 @@ android:title="@string/application_appearance"> + android:summary="@string/icon_pack_choose_summary" /> diff --git a/app/src/main/res/xml/application_preferences.xml b/app/src/main/res/xml/application_preferences.xml index 152e7c253..e5fb43863 100644 --- a/app/src/main/res/xml/application_preferences.xml +++ b/app/src/main/res/xml/application_preferences.xml @@ -24,15 +24,15 @@ android:title="@string/general"> + android:summary="@string/allow_no_password_summary" + android:defaultValue="@bool/allow_no_password_default"/> + android:summary="@string/enable_read_only_summary" + android:defaultValue="@bool/enable_read_only_default"/> @@ -40,10 +40,10 @@ android:title="@string/search_label"> + android:summary="@string/omitbackup_summary" + android:defaultValue="@bool/omitbackup_default"/> @@ -100,10 +100,10 @@ android:title="@string/history"> + android:summary="@string/recentfile_summary" + android:defaultValue="@bool/recentfile_default"/> + android:defaultValue="@bool/settings_saf_default"/> + android:defaultValue="@bool/auto_open_file_uri_default"/> diff --git a/app/src/main/res/xml/form_filling_preferences.xml b/app/src/main/res/xml/form_filling_preferences.xml index 71fe140b0..4de7e0058 100644 --- a/app/src/main/res/xml/form_filling_preferences.xml +++ b/app/src/main/res/xml/form_filling_preferences.xml @@ -55,10 +55,10 @@ + android:defaultValue="@bool/settings_autofill_enable_default"/> \ No newline at end of file From ff7f0e0a600b2068973236f3478c4bc06c639e11 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 21 Feb 2019 16:25:00 +0100 Subject: [PATCH 058/289] Add option for back lock --- .../keepass/activities/GroupActivity.java | 14 +++++++++++++- .../keepass/settings/PreferencesUtil.java | 7 ++++++- app/src/main/res/values/donottranslate.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/application_preferences.xml | 5 +++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 9061e63fe..57e530b4b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -1214,7 +1214,19 @@ public class GroupActivity extends LockingActivity @Override public void onBackPressed() { - super.onBackPressed(); + + // Normal way when we are not in root + if (!rootGroup.equals(mCurrentGroup)) + super.onBackPressed(); + // Else lock if needed + else { + if (PreferencesUtil.isLockDatabaseWhenBackButtonOnRootClicked(this)) { + App.getDB().clear(getApplicationContext()); + super.onBackPressed(); + } else { + moveTaskToBack(true); + } + } listNodesFragment = (ListNodesFragment) getSupportFragmentManager().findFragmentByTag(LIST_NODES_FRAGMENT_TAG); // to refresh fragment diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java index e25c3d538..e40d75832 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java @@ -22,7 +22,6 @@ package com.kunzisoft.keepass.settings; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.util.TypedValue; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.database.SortNodeEnum; @@ -99,6 +98,12 @@ public class PreferencesUtil { ctx.getResources().getBoolean(R.bool.lock_database_screen_off_default)); } + public static boolean isLockDatabaseWhenBackButtonOnRootClicked(Context ctx) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); + return prefs.getBoolean(ctx.getString(R.string.lock_database_back_root_key), + ctx.getResources().getBoolean(R.bool.lock_database_back_root_default)); + } + public static boolean isFingerprintEnable(Context ctx) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); return prefs.getBoolean(ctx.getString(R.string.fingerprint_enable_key), diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index a87461203..4155e7112 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -76,6 +76,8 @@ app_timeout_key lock_database_screen_off_key true + lock_database_back_root_key + true password_length_key list_password_generator_options_key maskpass diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d4d44cce..0ac961cdd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -230,6 +230,8 @@ Lock Screen lock Lock the database when the screen is off + Back 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 diff --git a/app/src/main/res/xml/application_preferences.xml b/app/src/main/res/xml/application_preferences.xml index e5fb43863..4436828df 100644 --- a/app/src/main/res/xml/application_preferences.xml +++ b/app/src/main/res/xml/application_preferences.xml @@ -63,6 +63,11 @@ android:title="@string/lock_database_screen_off_title" android:summary="@string/lock_database_screen_off_summary" android:defaultValue="@bool/lock_database_screen_off_default"/> + From da86a971b59fc097adc4919d18fe7683eb798119 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 22 Feb 2019 13:34:07 +0100 Subject: [PATCH 059/289] Refactor Autofill and Keyboard selection --- app/src/main/AndroidManifest.xml | 4 +- .../keepass/activities/EntryActivity.java | 2 +- .../keepass/activities/EntryEditActivity.java | 4 +- .../keepass/activities/GroupActivity.java | 174 ++++++++---------- .../activities/lock/LockingActivity.kt | 4 +- ...ctivity.kt => AutoFillLauncherActivity.kt} | 12 +- .../keepass/autofill/AutofillHelper.java | 167 ----------------- .../keepass/autofill/AutofillHelper.kt | 147 +++++++++++++++ .../keepass/autofill/KeeAutofillService.java | 2 +- .../kunzisoft/keepass/database/PwEntry.java | 17 ++ .../fileselect/FileSelectActivity.java | 161 +++++++--------- .../keepass/magikeyboard/KeyboardHelper.kt | 14 ++ .../magikeyboard/KeyboardLauncherActivity.kt | 27 +++ .../keepass/magikeyboard/MagikIME.java | 4 +- .../keepass/password/PasswordActivity.java | 73 +++----- .../selection/EntrySelectionHelper.java | 71 ------- .../keepass/selection/EntrySelectionHelper.kt | 36 ++++ .../KeyboardEntryRetrieverActivity.kt | 62 ------- .../keepass/settings/PreferencesUtil.java | 6 + .../keepass/settings/SettingsActivity.kt | 2 +- .../keepass/timeout/TimeoutHelper.kt | 11 +- 21 files changed, 441 insertions(+), 559 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/autofill/{AutoFillAuthActivity.kt => AutoFillLauncherActivity.kt} (87%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d9f486a01..b887db2a2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -126,10 +126,10 @@ android:configChanges="orientation|keyboardHidden" android:windowSoftInputMode="stateHidden" /> - - activity.startActivityForResult(intent, 0)); } @@ -179,16 +179,15 @@ public class GroupActivity extends LockingActivity * ------------------------- */ - public static void launchForKeyboardResult(Activity activity, boolean readOnly) { - TimeoutHelper.INSTANCE.recordTime(activity); - launchForKeyboardResult(activity, null, readOnly); + public static void launchForKeyboardSelection(Activity activity, boolean readOnly) { + launchForKeyboardSelection(activity, null, readOnly); } - public static void launchForKeyboardResult(Activity activity, PwGroup group, boolean readOnly) { + public static void launchForKeyboardSelection(Activity activity, PwGroup group, boolean readOnly) { // TODO implement pre search to directly open the direct group + TimeoutHelper.INSTANCE.recordTime(activity); buildAndLaunchIntent(activity, group, readOnly, (intent) -> { - EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent); - activity.startActivityForResult(intent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE); + KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent); }); } @@ -199,26 +198,17 @@ public class GroupActivity extends LockingActivity */ @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity activity, AssistStructure assistStructure, boolean readOnly) { - if ( assistStructure != null ) { - TimeoutHelper.INSTANCE.recordTime(activity); - launchForAutofillResult(activity, null, assistStructure, readOnly); - } else { - launch(activity, readOnly); - } + public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure, boolean readOnly) { + launchForAutofillResult(activity, null, assistStructure, readOnly); } @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity activity, PwGroup group, AssistStructure assistStructure, boolean readOnly) { + public static void launchForAutofillResult(Activity activity, PwGroup group, @NonNull AssistStructure assistStructure, boolean readOnly) { // TODO implement pre search to directly open the direct group - if ( assistStructure != null ) { - buildAndLaunchIntent(activity, group, readOnly, (intent) -> { - AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure); - activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE); - }); - } else { - launch(activity, group, readOnly); - } + TimeoutHelper.INSTANCE.recordTime(activity); + buildAndLaunchIntent(activity, group, readOnly, (intent) -> { + AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, intent, assistStructure); + }); } @Override @@ -316,13 +306,6 @@ public class GroupActivity extends LockingActivity addNodeButtonView.setAddEntryClickListener(v -> EntryEditActivity.launch(GroupActivity.this, mCurrentGroup)); - // To init autofill - entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - autofillHelper = new AutofillHelper(); - autofillHelper.retrieveAssistStructure(getIntent()); - } - // Search suggestion searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database); @@ -507,45 +490,44 @@ public class GroupActivity extends LockingActivity @Override public void onNodeClick(PwNode node) { - - // Add event when we have Autofill - AssistStructure assistStructure = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - assistStructure = autofillHelper.getAssistStructure(); - if (assistStructure != null) { - switch (node.getType()) { - case GROUP: - openChildGroup((PwGroup) node); - break; - case ENTRY: - // Build response with the entry selected - autofillHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); - finish(); - break; + switch (node.getType()) { + case GROUP: + try { + openChildGroup((PwGroup) node); + } catch (ClassCastException e) { + Log.e(TAG, "Node can't be cast in Group"); } - } - } - if ( assistStructure == null ){ - if (entrySelectionMode) { - switch (node.getType()) { - case GROUP: - openChildGroup((PwGroup) node); - break; - case ENTRY: - EntrySelectionHelper.buildResponseWhenEntrySelected(this, (PwEntry) node); - finish(); - break; + break; + case ENTRY: + try { + PwEntry entry = ((PwEntry) node); + EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), + () -> { + EntryActivity.launch(GroupActivity.this, entry, getReadOnly()); + return null; + }, + () -> { + MagikIME.setEntryKey(entry.getEntry()); + // Show the notification if allowed in Preferences + if (PreferencesUtil.enableKeyboardNotificationEntry(GroupActivity.this)) { + Intent notificationIntent = new Intent(GroupActivity.this, KeyboardEntryNotificationService.class); + startService(notificationIntent); + } + moveTaskToBack(true); + return null; + }, + assistStructure -> { + // Build response with the entry selected + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.INSTANCE.buildResponseWhenEntrySelected(GroupActivity.this, entry); + } + finish(); + return null; + }); + } catch (ClassCastException e) { + Log.e(TAG, "Node can't be cast in Entry"); } - } else { - switch (node.getType()) { - case GROUP: - openChildGroup((PwGroup) node); - break; - case ENTRY: - EntryActivity.launch(this, (PwEntry) node, getReadOnly()); - break; - } - } + break; } } @@ -920,7 +902,6 @@ public class GroupActivity extends LockingActivity @Override public void startActivity(Intent intent) { - boolean customSearchQueryExecuted = false; // Get the intent, verify the action and get the query if (Intent.ACTION_SEARCH.equals(intent.getAction())) { @@ -931,22 +912,26 @@ public class GroupActivity extends LockingActivity searchIntent.setAction(Intent.ACTION_SEARCH); searchIntent.putExtra(SearchManager.QUERY, query); - if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && autofillHelper.getAssistStructure() != null ) { - AutofillHelper.addAssistStructureExtraInIntent(searchIntent, autofillHelper.getAssistStructure()); - startActivityForResult(searchIntent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE); - customSearchQueryExecuted = true; - } - // To get the keyboard response, verify if the current intent contains the EntrySelection key - else if (EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent())){ - EntrySelectionHelper.addEntrySelectionModeExtraInIntent(searchIntent); - startActivityForResult(searchIntent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE); - customSearchQueryExecuted = true; - } - } - - if (!customSearchQueryExecuted) { - super.startActivity(intent); + EntrySelectionHelper.INSTANCE.doEntrySelectionAction(intent, + () -> { + GroupActivity.super.startActivity(intent); + return null; + }, + () -> { + KeyboardHelper.INSTANCE.startActivityForKeyboardSelection( + GroupActivity.this, + searchIntent); + return null; + }, + assistStructure -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.INSTANCE.startActivityForAutofillResult( + GroupActivity.this, + searchIntent, + assistStructure); + } + return null; + }); } } @@ -1179,9 +1164,8 @@ public class GroupActivity extends LockingActivity protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index 2a6d9fe90..5601d9433 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -103,7 +103,7 @@ abstract class LockingActivity : StylishActivity() { // After the first creation // or If simply swipe with another application // If the time is out -> close the Activity - TimeoutHelper.checkTime(this) + TimeoutHelper.checkTimeAndLockIfTimeout(this) // If onCreate already record time if (!exitLock) TimeoutHelper.recordTime(this) @@ -121,7 +121,7 @@ abstract class LockingActivity : StylishActivity() { if (timeoutEnable) { // If the time is out during our navigation in activity -> close the Activity - TimeoutHelper.checkTime(this) + TimeoutHelper.checkTimeAndLockIfTimeout(this) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt similarity index 87% rename from app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt rename to app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt index aa92f16e4..d04a28fd6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillAuthActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -34,12 +34,12 @@ import com.kunzisoft.keepass.fileselect.FileSelectActivity import com.kunzisoft.keepass.timeout.TimeoutHelper @RequiresApi(api = Build.VERSION_CODES.O) -class AutoFillAuthActivity : AppCompatActivity() { +class AutoFillLauncherActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { - TimeoutHelper.checkTime(this) {} // Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE) - val assistStructure = AutofillHelper().retrieveAssistStructure(intent) + TimeoutHelper.checkTime(this) + val assistStructure = AutofillHelper.retrieveAssistStructure(intent) if (assistStructure != null) { if (App.getDB().loaded) GroupActivity.launchForAutofillResult(this, assistStructure, true) @@ -54,14 +54,14 @@ class AutoFillAuthActivity : AppCompatActivity() { super.onCreate(savedInstanceState) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) } companion object { fun getAuthIntentSenderForResponse(context: Context): IntentSender { - val intent = Intent(context, AutoFillAuthActivity::class.java) + val intent = Intent(context, AutoFillLauncherActivity::class.java) return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT).intentSender } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.java b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.java deleted file mode 100644 index fa2798ce4..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.autofill; - -import android.app.Activity; -import android.app.assist.AssistStructure; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.service.autofill.Dataset; -import android.service.autofill.FillResponse; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresApi; -import android.util.Log; -import android.view.autofill.AutofillId; -import android.view.autofill.AutofillManager; -import android.view.autofill.AutofillValue; -import android.widget.RemoteViews; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.selection.EntrySelectionHelper; - -import java.util.ArrayList; -import java.util.List; - - -@RequiresApi(api = Build.VERSION_CODES.O) -public class AutofillHelper { - - public static final int AUTOFILL_RESPONSE_REQUEST_CODE = 8165; - - private AssistStructure assistStructure = null; - - public AssistStructure retrieveAssistStructure(Intent intent) { - if (intent != null && intent.getExtras() != null) { - assistStructure = intent.getParcelableExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE); - } - return assistStructure; - } - - /** - * Call retrieveAssistStructure before - */ - public AssistStructure getAssistStructure() { - return assistStructure; - } - - public static void addAssistStructureExtraInIntent(Intent intent, AssistStructure assistStructure) { - if (assistStructure != null) { - EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent); - intent.putExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE, assistStructure); - } - } - - /** - * Define if android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE is an extra bundle key present in the Intent - */ - public static boolean isIntentContainsExtraAssistStructureKey(Intent intent) { - return (intent != null - && intent.getExtras() != null - && intent.getExtras().containsKey(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE)); - } - - private @Nullable Dataset buildDataset(Context context, PwEntry entry, - StructureParser.Result struct) { - String title = makeEntryTitle(entry); - RemoteViews views = newRemoteViews(context.getPackageName(), title); - Dataset.Builder builder = new Dataset.Builder(views); - builder.setId(entry.getUUID().toString()); - - if (entry.getPassword() != null) { - AutofillValue value = AutofillValue.forText(entry.getPassword()); - struct.password.forEach(id -> builder.setValue(id, value)); - } - if (entry.getUsername() != null) { - AutofillValue value = AutofillValue.forText(entry.getUsername()); - List ids = new ArrayList<>(struct.username); - if (entry.getUsername().contains("@") || struct.username.isEmpty()) - ids.addAll(struct.email); - ids.forEach(id -> builder.setValue(id, value)); - } - try { - return builder.build(); - } catch (IllegalArgumentException e) { - // if not value be set - return null; - } - } - - static private String makeEntryTitle(PwEntry entry) { - if (!entry.getTitle().isEmpty() && !entry.getUsername().isEmpty()) - return String.format("%s (%s)", entry.getTitle(), entry.getUsername()); - if (!entry.getTitle().isEmpty()) - return entry.getTitle(); - if (!entry.getUsername().isEmpty()) - return entry.getUsername(); - if (!entry.getNotes().isEmpty()) - return entry.getNotes().trim(); - return ""; // TODO No title - } - - /** - * Method to hit when right key is selected - */ - public void buildResponseWhenEntrySelected(Activity activity, PwEntry entry) { - Intent mReplyIntent; - Intent intent = activity.getIntent(); - if (isIntentContainsExtraAssistStructureKey(intent)) { - AssistStructure structure = intent.getParcelableExtra(android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE); - StructureParser.Result result = new StructureParser(structure).parse(); - - // New Response - FillResponse.Builder responseBuilder = new FillResponse.Builder(); - Dataset dataset = buildDataset(activity, entry, result); - responseBuilder.addDataset(dataset); - mReplyIntent = new Intent(); - Log.d(activity.getClass().getName(), "Successed Autofill auth."); - mReplyIntent.putExtra( - AutofillManager.EXTRA_AUTHENTICATION_RESULT, - responseBuilder.build()); - activity.setResult(Activity.RESULT_OK, mReplyIntent); - } else { - Log.w(activity.getClass().getName(), "Failed Autofill auth."); - activity.setResult(Activity.RESULT_CANCELED); - } - } - - /** - * Utility method to loop and close each activity with return data - */ - public static void onActivityResultSetResultAndFinish(Activity activity, int requestCode, int resultCode, Intent data) { - if (requestCode == AUTOFILL_RESPONSE_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - activity.setResult(resultCode, data); - } - if (resultCode == Activity.RESULT_CANCELED) { - activity.setResult(Activity.RESULT_CANCELED); - } - activity.finish(); - } - } - - private static RemoteViews newRemoteViews(String packageName, String remoteViewsText) { - RemoteViews presentation = - new RemoteViews(packageName, R.layout.autofill_service_list_item); - presentation.setTextViewText(R.id.text, remoteViewsText); - return presentation; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt new file mode 100644 index 000000000..9048a4558 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -0,0 +1,147 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.autofill + +import android.app.Activity +import android.app.assist.AssistStructure +import android.content.Context +import android.content.Intent +import android.os.Build +import android.service.autofill.Dataset +import android.service.autofill.FillResponse +import android.support.annotation.RequiresApi +import android.util.Log +import android.view.autofill.AutofillManager +import android.view.autofill.AutofillValue +import android.widget.RemoteViews +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.PwEntry +import com.kunzisoft.keepass.selection.EntrySelectionHelper +import java.util.* + + +@RequiresApi(api = Build.VERSION_CODES.O) +object AutofillHelper { + + private const val AUTOFILL_RESPONSE_REQUEST_CODE = 8165 + + private const val ASSIST_STRUCTURE = android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE + + fun retrieveAssistStructure(intent: Intent?): AssistStructure? { + intent?.let { + return it.getParcelableExtra(ASSIST_STRUCTURE) + } + return null + } + + private fun makeEntryTitle(entry: PwEntry<*>): String { + if (!entry.title.isEmpty() && !entry.username.isEmpty()) + return String.format("%s (%s)", entry.title, entry.username) + if (!entry.title.isEmpty()) + return entry.title + if (!entry.username.isEmpty()) + return entry.username + return if (!entry.notes.isEmpty()) entry.notes.trim { it <= ' ' } else "" + // TODO No title + } + + private fun buildDataset(context: Context, entry: PwEntry<*>, + struct: StructureParser.Result): Dataset? { + val title = makeEntryTitle(entry) + val views = newRemoteViews(context.packageName, title) + val builder = Dataset.Builder(views) + builder.setId(entry.uuid.toString()) + + if (entry.password != null) { + val value = AutofillValue.forText(entry.password) + struct.password.forEach { id -> builder.setValue(id, value) } + } + if (entry.username != null) { + val value = AutofillValue.forText(entry.username) + val ids = ArrayList(struct.username) + if (entry.username.contains("@") || struct.username.isEmpty()) + ids.addAll(struct.email) + ids.forEach { id -> builder.setValue(id, value) } + } + return try { + builder.build() + } catch (e: IllegalArgumentException) { + // if not value be set + null + } + } + + /** + * Method to hit when right key is selected + */ + fun buildResponseWhenEntrySelected(activity: Activity, entry: PwEntry<*>) { + val mReplyIntent: Intent + activity.intent?.let { intent -> + if (intent.extras.containsKey(ASSIST_STRUCTURE)) { + val structure = intent.getParcelableExtra(ASSIST_STRUCTURE) + val result = StructureParser(structure).parse() + + // New Response + val responseBuilder = FillResponse.Builder() + val dataset = buildDataset(activity, entry, result) + responseBuilder.addDataset(dataset) + mReplyIntent = Intent() + Log.d(activity.javaClass.name, "Successed Autofill auth.") + mReplyIntent.putExtra( + AutofillManager.EXTRA_AUTHENTICATION_RESULT, + responseBuilder.build()) + activity.setResult(Activity.RESULT_OK, mReplyIntent) + } else { + Log.w(activity.javaClass.name, "Failed Autofill auth.") + activity.setResult(Activity.RESULT_CANCELED) + } + } + } + + /** + * Utility method to start an activity with an Autofill for result + */ + fun startActivityForAutofillResult(activity: Activity, intent: Intent, assistStructure: AssistStructure) { + EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent) + intent.putExtra(ASSIST_STRUCTURE, assistStructure) + activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE) + } + + /** + * Utility method to loop and close each activity with return data + */ + fun onActivityResultSetResultAndFinish(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == AUTOFILL_RESPONSE_REQUEST_CODE) { + if (resultCode == Activity.RESULT_OK) { + activity.setResult(resultCode, data) + } + if (resultCode == Activity.RESULT_CANCELED) { + activity.setResult(Activity.RESULT_CANCELED) + } + activity.finish() + } + } + + private fun newRemoteViews(packageName: String, remoteViewsText: String): RemoteViews { + val presentation = RemoteViews(packageName, R.layout.autofill_service_list_item) + presentation.setTextViewText(R.id.text, remoteViewsText) + return presentation + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java index 932509972..eb62f6a48 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java @@ -62,7 +62,7 @@ public class KeeAutofillService extends AutofillService { if (!Arrays.asList(autofillIds).isEmpty()) { // If the entire Autofill Response is authenticated, AuthActivity is used // to generate Response. - IntentSender sender = AutoFillAuthActivity.Companion.getAuthIntentSenderForResponse(this); + IntentSender sender = AutoFillLauncherActivity.Companion.getAuthIntentSenderForResponse(this); RemoteViews presentation = new RemoteViews(getPackageName(), R.layout.autofill_service_unlock); responseBuilder.setAuthentication(autofillIds, sender, presentation); callback.onSuccess(responseBuilder.build()); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java b/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java index fb1434aa0..8f72c888c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java @@ -23,6 +23,8 @@ import android.os.Parcel; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.model.Entry; +import com.kunzisoft.keepass.model.Field; import java.util.UUID; @@ -199,6 +201,21 @@ public abstract class PwEntry extends PwNode { return false; } + public Entry getEntry() { + Entry entryModel = new Entry(); + entryModel.setTitle(getTitle()); + entryModel.setUsername(getUsername()); + entryModel.setPassword(getPassword()); + entryModel.setUrl(getUrl()); + if (containsCustomFields()) { + getFields() + .doActionToAllCustomProtectedField( + (key, value) -> entryModel.addCustomField( + new Field(key, value.toString()))); + } + return entryModel; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java index 966ea388b..d1b9115e0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java @@ -53,9 +53,9 @@ import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable; import com.kunzisoft.keepass.database.action.FileOnFinishRunnable; -import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment; +import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.password.AssignPasswordHelper; import com.kunzisoft.keepass.password.PasswordActivity; import com.kunzisoft.keepass.selection.EntrySelectionHelper; @@ -104,9 +104,6 @@ public class FileSelectActivity extends StylishActivity implements // TODO Consultation Mode private boolean consultationMode = false; - private boolean entrySelectionMode; - private AutofillHelper autofillHelper; - private View fileSelectExpandableButton; private ExpandableLayout fileSelectExpandable; private EditText openFileNameView; @@ -118,27 +115,33 @@ public class FileSelectActivity extends StylishActivity implements private String defaultPath; - public static void launch(Activity activity) { - Intent intent = new Intent(activity, FileSelectActivity.class); - // only to avoid visible flickering when redirecting - activity.startActivityForResult(intent, RESULT_CANCELED); - } + /* + * ------------------------- + * No Standard Launch, pass by PasswordActivity + * ------------------------- + */ - public static void launchForKeyboardResult(Activity activity) { - Intent intent = new Intent(activity, FileSelectActivity.class); - EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent); - activity.startActivityForResult(intent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE); + /* + * ------------------------- + * Keyboard Launch + * ------------------------- + */ + + public static void launchForKeyboardSelection(Activity activity) { + KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, new Intent(activity, FileSelectActivity.class)); } + /* + * ------------------------- + * Autofill Launch + * ------------------------- + */ + @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity activity, AssistStructure assistStructure) { - if ( assistStructure != null ) { - Intent intent = new Intent(activity, FileSelectActivity.class); - AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure); - activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE); - } else { - launch(activity); - } + public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure) { + AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, + new Intent(activity, FileSelectActivity.class), + assistStructure); } @Override @@ -177,13 +180,6 @@ public class FileSelectActivity extends StylishActivity implements RecyclerView mListFiles = findViewById(R.id.file_list); mListFiles.setLayoutManager(new LinearLayoutManager(this)); - entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent()); - // To retrieve info for AutoFill - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - autofillHelper = new AutofillHelper(); - autofillHelper.retrieveAssistStructure(getIntent()); - } - // Open button openButtonView = findViewById(R.id.open_database); openButtonView.setOnClickListener(v -> { @@ -243,40 +239,52 @@ public class FileSelectActivity extends StylishActivity implements checkAndPerformedEducation(); } + private void fileNoFoundAction(FileNotFoundException e) { + String error = getString(R.string.file_not_found_content); + Toast.makeText(FileSelectActivity.this, + error, Toast.LENGTH_LONG).show(); + Log.e(TAG, error, e); + } + + private void launchPasswordActivity(String fileName, String keyFile) { + EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), + () -> { + try { + PasswordActivity.launch(FileSelectActivity.this, + fileName, keyFile); + } catch (FileNotFoundException e) { + fileNoFoundAction(e); + } + return null; + }, + () -> { + try { + PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, + fileName, keyFile); + } catch (FileNotFoundException e) { + fileNoFoundAction(e); + } + return null; + }, + assistStructure -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + PasswordActivity.launchForAutofillResult(FileSelectActivity.this, + fileName, keyFile, + assistStructure); + } catch (FileNotFoundException e) { + fileNoFoundAction(e); + } + } + return null; + }); + } + private void launchPasswordActivityWithPath(String path) { - try { - AssistStructure assistStructure = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - assistStructure = autofillHelper.getAssistStructure(); - if (assistStructure != null) { - PasswordActivity.launchForAutofillResult(FileSelectActivity.this, - path, - assistStructure); - } - } - if (assistStructure == null) { - if (entrySelectionMode) { - PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, path); - } else { - PasswordActivity.launch(FileSelectActivity.this, path); - } - } - // Delete flickering for kitkat <= - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) - overridePendingTransition(0, 0); - } catch (ContentFileNotFoundException e) { - String error = getString(R.string.file_not_found_content); - Toast.makeText(FileSelectActivity.this, - error, Toast.LENGTH_LONG).show(); - Log.e(TAG, error, e); - } catch (FileNotFoundException e) { - String error = getString(R.string.file_not_found); - Toast.makeText(FileSelectActivity.this, - error, Toast.LENGTH_LONG).show(); - Log.e(TAG, error, e); - } catch (Exception e) { - Log.e(TAG, "Can't launch PasswordActivity", e); - } + launchPasswordActivity(path, ""); + // Delete flickering for kitkat <= + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + overridePendingTransition(0, 0); } private void updateExternalStorageWarning() { @@ -603,32 +611,7 @@ public class FileSelectActivity extends StylishActivity implements @Override public void onFileItemOpenListener(int itemPosition) { new OpenFileHistoryAsyncTask((fileName, keyFile) -> { - // TODO ENCAPSULATE - try { - AssistStructure assistStructure = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - assistStructure = autofillHelper.getAssistStructure(); - if (assistStructure != null) { - PasswordActivity.launchForAutofillResult(FileSelectActivity.this, - fileName, keyFile, assistStructure); - } - } - if (assistStructure == null) { - if (entrySelectionMode) { - PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, fileName, keyFile); - } else { - PasswordActivity.launch(FileSelectActivity.this, fileName, keyFile); - } - } - } catch (ContentFileNotFoundException e) { - Toast.makeText(FileSelectActivity.this, - R.string.file_not_found_content, Toast.LENGTH_LONG) - .show(); - } catch (FileNotFoundException e) { - Toast.makeText(FileSelectActivity.this, - R.string.file_not_found, Toast.LENGTH_LONG) - .show(); - } + launchPasswordActivity(fileName, keyFile); updateFileListVisibility(); }, fileHistory).execute(itemPosition); } @@ -656,10 +639,8 @@ public class FileSelectActivity extends StylishActivity implements protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - // Get the entry result in entry selection mode - EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); } keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt new file mode 100644 index 000000000..b6b9b37bf --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt @@ -0,0 +1,14 @@ +package com.kunzisoft.keepass.magikeyboard + +import android.app.Activity +import android.content.Intent +import com.kunzisoft.keepass.selection.EntrySelectionHelper + +object KeyboardHelper { + + fun startActivityForKeyboardSelection(activity: Activity, intent: Intent) { + EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent) + // only to avoid visible flickering when redirecting + activity.startActivityForResult(intent, 0) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt new file mode 100644 index 000000000..0262d27cc --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt @@ -0,0 +1,27 @@ +package com.kunzisoft.keepass.magikeyboard + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import com.kunzisoft.keepass.activities.GroupActivity +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.fileselect.FileSelectActivity +import com.kunzisoft.keepass.timeout.TimeoutHelper + +class KeyboardLauncherActivity : AppCompatActivity() { + + companion object { + val TAG = KeyboardLauncherActivity::class.java.name!! + } + + override fun onCreate(savedInstanceState: Bundle?) { + TimeoutHelper.checkTime(this) + if (App.getDB().loaded) + GroupActivity.launchForKeyboardSelection(this, true) + else { + // Pass extra to get entry + FileSelectActivity.launchForKeyboardSelection(this) + } + finish() + super.onCreate(savedInstanceState) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java index 5d7272189..5b8bb8dba 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java @@ -48,7 +48,6 @@ import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter; import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver; import com.kunzisoft.keepass.magikeyboard.view.MagikeyboardView; import com.kunzisoft.keepass.model.Entry; -import com.kunzisoft.keepass.selection.KeyboardEntryRetrieverActivity; import static com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.LOCK_ACTION; @@ -206,8 +205,7 @@ public class MagikIME extends InputMethodService break; case KEY_ENTRY: deleteEntryKey(this); - Intent intent = new Intent(this, KeyboardEntryRetrieverActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent intent = new Intent(this, KeyboardLauncherActivity.class); startActivity(intent); break; case KEY_LOCK: diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 64fcfea40..b0b1bebbf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -70,6 +70,7 @@ import com.kunzisoft.keepass.fileselect.KeyFileHelper; import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; +import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.selection.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; @@ -133,9 +134,6 @@ public class PasswordActivity extends StylishActivity private KeyFileHelper keyFileHelper; - protected boolean entrySelectionMode; - private AutofillHelper autofillHelper; - private static void buildAndLaunchIntent(Activity activity, String fileName, String keyFile, IntentBuildLauncher intentBuildLauncher) { Intent intent = new Intent(activity, PasswordActivity.class); @@ -167,12 +165,6 @@ public class PasswordActivity extends StylishActivity * ------------------------- */ - public static void launch( - Activity act, - String fileName) throws FileNotFoundException { - launch(act, fileName, ""); - } - public static void launch( Activity activity, String fileName, @@ -187,12 +179,6 @@ public class PasswordActivity extends StylishActivity * ------------------------- */ - public static void launchForKeyboardResult( - Activity act, - String fileName) throws FileNotFoundException { - launchForKeyboardResult(act, fileName, ""); - } - public static void launchForKeyboardResult( Activity activity, String fileName, @@ -200,9 +186,7 @@ public class PasswordActivity extends StylishActivity verifyFileNameUriFromLaunch(fileName); buildAndLaunchIntent(activity, fileName, keyFile, (intent) -> { - EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent); - // only to avoid visible flickering when redirecting - activity.startActivityForResult(intent, EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE); + KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent); }); } @@ -212,14 +196,6 @@ public class PasswordActivity extends StylishActivity * ------------------------- */ - @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult( - Activity act, - String fileName, - AssistStructure assistStructure) throws FileNotFoundException { - launchForAutofillResult(act, fileName, "", assistStructure); - } - @RequiresApi(api = Build.VERSION_CODES.O) public static void launchForAutofillResult( Activity activity, @@ -230,8 +206,10 @@ public class PasswordActivity extends StylishActivity if ( assistStructure != null ) { buildAndLaunchIntent(activity, fileName, keyFile, (intent) -> { - AutofillHelper.addAssistStructureExtraInIntent(intent, assistStructure); - activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE); + AutofillHelper.INSTANCE.startActivityForAutofillResult( + activity, + intent, + assistStructure); }); } else { launch(activity, fileName, keyFile); @@ -310,13 +288,6 @@ public class PasswordActivity extends StylishActivity fingerPrintAnimatedVector = new FingerPrintAnimatedVector(this, fingerprintImageView); } - - entrySelectionMode = EntrySelectionHelper.isIntentInEntrySelectionMode(getIntent()); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - autofillHelper = new AutofillHelper(); - autofillHelper.retrieveAssistStructure(getIntent()); - } } @Override @@ -974,20 +945,21 @@ public class PasswordActivity extends StylishActivity } private void launchGroupActivity() { - AssistStructure assistStructure = null; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - assistStructure = autofillHelper.getAssistStructure(); - if (assistStructure != null) { - GroupActivity.launchForAutofillResult(PasswordActivity.this, assistStructure, readOnly); - } - } - if (assistStructure == null) { - if (entrySelectionMode) { - GroupActivity.launchForKeyboardResult(PasswordActivity.this, readOnly); - } else { - GroupActivity.launch(PasswordActivity.this, readOnly); - } - } + EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), + () -> { + GroupActivity.launch(PasswordActivity.this, readOnly); + return null; + }, + () -> { + GroupActivity.launchForKeyboardSelection(PasswordActivity.this, readOnly); + return null; + }, + assistStructure -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + GroupActivity.launchForAutofillResult(PasswordActivity.this, assistStructure, readOnly); + } + return null; + }); } @Override @@ -1060,9 +1032,8 @@ public class PasswordActivity extends StylishActivity super.onActivityResult(requestCode, resultCode, data); // To get entry in result - EntrySelectionHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); } boolean keyFileResult = false; diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java deleted file mode 100644 index 71c9db809..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.kunzisoft.keepass.selection; - -import android.app.Activity; -import android.content.Intent; -import android.util.Log; - -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.model.Entry; -import com.kunzisoft.keepass.model.Field; - -public class EntrySelectionHelper { - - public static final int ENTRY_SELECTION_RESPONSE_REQUEST_CODE = 5164; - - public static final String EXTRA_ENTRY_SELECTION_MODE = "com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE"; - - public static void addEntrySelectionModeExtraInIntent(Intent intent) { - intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, true); - } - - public static boolean isIntentInEntrySelectionMode(Intent intent) { - return intent.getBooleanExtra(EXTRA_ENTRY_SELECTION_MODE, false); - } - - /** - * Method to hit when right key is selected - */ - public static void buildResponseWhenEntrySelected(Activity activity, PwEntry entry) { - Intent mReplyIntent; - Intent intent = activity.getIntent(); - boolean entrySelectionMode = isIntentInEntrySelectionMode(intent); - if (entrySelectionMode) { - mReplyIntent = new Intent(); - - Entry entryModel = new Entry(); - entryModel.setTitle(entry.getTitle()); - entryModel.setUsername(entry.getUsername()); - entryModel.setPassword(entry.getPassword()); - entryModel.setUrl(entry.getUrl()); - if (entry.containsCustomFields()) { - entry.getFields() - .doActionToAllCustomProtectedField( - (key, value) -> entryModel.addCustomField( - new Field(key, value.toString()))); - } - Log.d(activity.getClass().getName(), "Build entry selection for reply: " + entryModel.getTitle()); - - mReplyIntent.putExtra( - EXTRA_ENTRY_SELECTION_MODE, - entryModel); - activity.setResult(Activity.RESULT_OK, mReplyIntent); - } else { - activity.setResult(Activity.RESULT_CANCELED); - } - } - - /** - * Utility method to loop and close each activity with return data - */ - public static void onActivityResultSetResultAndFinish(Activity activity, int requestCode, int resultCode, Intent data) { - if (requestCode == ENTRY_SELECTION_RESPONSE_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - activity.setResult(resultCode, data); - } - if (resultCode == Activity.RESULT_CANCELED) { - activity.setResult(Activity.RESULT_CANCELED); - } - activity.finish(); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt new file mode 100644 index 000000000..3625fdd56 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt @@ -0,0 +1,36 @@ +package com.kunzisoft.keepass.selection + +import android.app.assist.AssistStructure +import android.content.Intent +import android.os.Build +import com.kunzisoft.keepass.autofill.AutofillHelper + +object EntrySelectionHelper { + + private const val EXTRA_ENTRY_SELECTION_MODE = "com.kunzisoft.keepass.extra.ENTRY_SELECTION_MODE" + + fun addEntrySelectionModeExtraInIntent(intent: Intent) { + intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, true) + } + + fun doEntrySelectionAction(intent: Intent, + standardAction: () -> Unit, + keyboardAction: () -> Unit, + autofillAction: (assistStructure: AssistStructure) -> Unit) { + var assistStructure: AssistStructure? = null + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + assistStructure = AutofillHelper.retrieveAssistStructure(intent) + assistStructure?.let { + autofillAction.invoke(assistStructure) + } + } + if (assistStructure == null) { + if (intent.getBooleanExtra(EXTRA_ENTRY_SELECTION_MODE, false)) { + intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, false) + keyboardAction.invoke() + } else { + standardAction.invoke() + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt b/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt deleted file mode 100644 index 8d4c37490..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/selection/KeyboardEntryRetrieverActivity.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.kunzisoft.keepass.selection - -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import android.preference.PreferenceManager -import android.support.v7.app.AppCompatActivity -import android.util.Log -import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.activities.GroupActivity -import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.fileselect.FileSelectActivity -import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService -import com.kunzisoft.keepass.magikeyboard.MagikIME -import com.kunzisoft.keepass.model.Entry -import com.kunzisoft.keepass.selection.EntrySelectionHelper.ENTRY_SELECTION_RESPONSE_REQUEST_CODE -import com.kunzisoft.keepass.selection.EntrySelectionHelper.EXTRA_ENTRY_SELECTION_MODE -import com.kunzisoft.keepass.timeout.TimeoutHelper - -class KeyboardEntryRetrieverActivity : AppCompatActivity() { - - companion object { - - val TAG = KeyboardEntryRetrieverActivity::class.java.name!! - } - - override fun onCreate(savedInstanceState: Bundle?) { - TimeoutHelper.checkTime(this) {} - if (App.getDB().loaded) - GroupActivity.launchForKeyboardResult(this, true) - else { - // Pass extra to get entry - FileSelectActivity.launchForKeyboardResult(this) - } - - super.onCreate(savedInstanceState) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - Log.d(TAG, "Retrieve the entry selected, requestCode: $requestCode, resultCode: $resultCode") - if (requestCode == ENTRY_SELECTION_RESPONSE_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - val entry = data?.getParcelableExtra(EXTRA_ENTRY_SELECTION_MODE) - Log.d(TAG, "Set the entry ${entry?.title} to keyboard") - MagikIME.setEntryKey(entry) - - // Show the notification if allowed in Preferences - val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) - if (sharedPreferences.getBoolean(getString(R.string.keyboard_notification_entry_key), - resources.getBoolean(R.bool.keyboard_notification_entry_default))) { - val notificationIntent = Intent(this, KeyboardEntryNotificationService::class.java) - startService(notificationIntent) - } - } - if (resultCode == Activity.RESULT_CANCELED) { - Log.w(TAG, "Entry not retrieved") - } - } - finish() - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java index e40d75832..499a664e5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java @@ -197,6 +197,12 @@ public class PreferencesUtil { context.getResources().getBoolean(R.bool.enable_read_only_default)); } + public static boolean enableKeyboardNotificationEntry(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean(context.getString(R.string.keyboard_notification_entry_key), + context.getResources().getBoolean(R.bool.keyboard_notification_entry_default)); + } + /** * All preference keys associated with education */ diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 5a27c5b31..9af21ceed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -48,7 +48,7 @@ open class SettingsActivity : LockingActivity(), MainPreferenceFragment.Callback intent.putExtra(TIMEOUT_ENABLE_KEY, timeoutEnable) if (!timeoutEnable) { activity.startActivity(intent) - } else if (TimeoutHelper.checkTime(activity)) { + } else if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { activity.startActivity(intent) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index 575e2b7a8..903ce1a8c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -78,9 +78,10 @@ object TimeoutHelper { } /** - * Check the time previously record with recordTime and do the shutdown action if timeout + * Check the time previously record with recordTime and do the [timeoutAction] if timeout + * return 'false' if timeout, 'true' if in time */ - fun checkTime(context: Context, shutdown: (() -> Unit)): Boolean { + fun checkTime(context: Context, timeoutAction: (() -> Unit)? = null): Boolean { // Cancel the lock PendingIntent if (App.getDB().loaded) { val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager @@ -118,7 +119,7 @@ object TimeoutHelper { if (diff >= appTimeout) { // We have timed out App.getDB().loaded = false - shutdown.invoke() + timeoutAction?.invoke() return false } return true @@ -127,14 +128,14 @@ object TimeoutHelper { /** * Check the time previously record with recordTime and lock the activity if timeout */ - fun checkTime(activity: Activity): Boolean { + fun checkTimeAndLockIfTimeout(activity: Activity): Boolean { return checkTime(activity) { activity.lock() } } fun lockOrResetTimeout(activity: Activity, action: (() -> Unit)? = null) { - if (checkTime(activity)) { + if (checkTimeAndLockIfTimeout(activity)) { recordTime(activity) action?.invoke() } From 3b559370858c268945bb25df853736d32b6fda1e Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 22 Feb 2019 15:10:07 +0100 Subject: [PATCH 060/289] Fix keyboard entry selection --- app/src/main/AndroidManifest.xml | 10 ++++++---- .../keepass/activities/GroupActivity.java | 20 +++++++++---------- .../fileselect/FileSelectActivity.java | 1 + .../keepass/magikeyboard/MagikIME.java | 6 +++++- .../keepass/password/PasswordActivity.java | 2 ++ .../keepass/selection/EntrySelectionHelper.kt | 2 +- 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b887db2a2..4f111ac2b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,10 +38,6 @@ - + + + diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 21cba3852..a7c88b8a1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -186,9 +186,8 @@ public class GroupActivity extends LockingActivity public static void launchForKeyboardSelection(Activity activity, PwGroup group, boolean readOnly) { // TODO implement pre search to directly open the direct group TimeoutHelper.INSTANCE.recordTime(activity); - buildAndLaunchIntent(activity, group, readOnly, (intent) -> { - KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent); - }); + buildAndLaunchIntent(activity, group, readOnly, + (intent) -> KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent)); } /* @@ -206,16 +205,15 @@ public class GroupActivity extends LockingActivity public static void launchForAutofillResult(Activity activity, PwGroup group, @NonNull AssistStructure assistStructure, boolean readOnly) { // TODO implement pre search to directly open the direct group TimeoutHelper.INSTANCE.recordTime(activity); - buildAndLaunchIntent(activity, group, readOnly, (intent) -> { - AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, intent, assistStructure); - }); + buildAndLaunchIntent(activity, group, readOnly, + (intent) -> AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, intent, assistStructure)); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if ( isFinishing() ) { + if (isFinishing()) { return; } @@ -510,10 +508,11 @@ public class GroupActivity extends LockingActivity MagikIME.setEntryKey(entry.getEntry()); // Show the notification if allowed in Preferences if (PreferencesUtil.enableKeyboardNotificationEntry(GroupActivity.this)) { - Intent notificationIntent = new Intent(GroupActivity.this, KeyboardEntryNotificationService.class); - startService(notificationIntent); + startService(new Intent( + GroupActivity.this, + KeyboardEntryNotificationService.class)); } - moveTaskToBack(true); + finish(); return null; }, assistStructure -> { @@ -921,6 +920,7 @@ public class GroupActivity extends LockingActivity KeyboardHelper.INSTANCE.startActivityForKeyboardSelection( GroupActivity.this, searchIntent); + finish(); return null; }, assistStructure -> { diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java index d1b9115e0..c9abd1741 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java @@ -261,6 +261,7 @@ public class FileSelectActivity extends StylishActivity implements try { PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, fileName, keyFile); + finish(); } catch (FileNotFoundException e) { fileNoFoundAction(e); } diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java index 5b8bb8dba..f8312a05c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java @@ -204,8 +204,12 @@ public class MagikIME extends InputMethodService // TODO Unlock key break; case KEY_ENTRY: - deleteEntryKey(this); + // Stop current service and reinit entry + stopService(new Intent(this, KeyboardEntryNotificationService.class)); + entryKey = null; Intent intent = new Intent(this, KeyboardLauncherActivity.class); + // New task needed because don't launch from an Activity context + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); break; case KEY_LOCK: diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index b0b1bebbf..04170ec9a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -952,6 +952,8 @@ public class PasswordActivity extends StylishActivity }, () -> { GroupActivity.launchForKeyboardSelection(PasswordActivity.this, readOnly); + // Do not keep history + finish(); return null; }, assistStructure -> { diff --git a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt index 3625fdd56..b65011149 100644 --- a/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/selection/EntrySelectionHelper.kt @@ -26,7 +26,7 @@ object EntrySelectionHelper { } if (assistStructure == null) { if (intent.getBooleanExtra(EXTRA_ENTRY_SELECTION_MODE, false)) { - intent.putExtra(EXTRA_ENTRY_SELECTION_MODE, false) + intent.removeExtra(EXTRA_ENTRY_SELECTION_MODE) keyboardAction.invoke() } else { standardAction.invoke() From 8426b1d91f6a6e6707cc56c8891edcce01e36e67 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 22 Feb 2019 16:13:40 +0100 Subject: [PATCH 061/289] Fix opening activity --- .../java/com/kunzisoft/keepass/activities/GroupActivity.java | 5 ++++- app/src/main/java/com/kunzisoft/keepass/app/App.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index a7c88b8a1..3539c339b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -254,6 +254,7 @@ public class GroupActivity extends LockingActivity } } + // TODO NULL getRootGroup() on null reference rootGroup = database.getPwDatabase().getRootGroup(); mCurrentGroup = retrieveCurrentGroup(getIntent(), savedInstanceState); currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); @@ -512,7 +513,7 @@ public class GroupActivity extends LockingActivity GroupActivity.this, KeyboardEntryNotificationService.class)); } - finish(); + moveTaskToBack(true); return null; }, assistStructure -> { @@ -932,6 +933,8 @@ public class GroupActivity extends LockingActivity } return null; }); + } else { + super.startActivity(intent); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.java b/app/src/main/java/com/kunzisoft/keepass/app/App.java index 7c8b4a99a..abec3ebf1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.java +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.java @@ -47,7 +47,7 @@ public class App extends MultiDexApplication { public static void setDB(Database d) { db = d; } - + public static Calendar getCalendar() { if ( calendar == null ) { calendar = Calendar.getInstance(); From 66e077f8f165b4d2733246cc78f5d1d62ae81c13 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 22 Feb 2019 16:47:24 +0100 Subject: [PATCH 062/289] Fix keyboard entry disappear --- .../main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java index f8312a05c..7ab991bf4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java @@ -209,7 +209,7 @@ public class MagikIME extends InputMethodService entryKey = null; Intent intent = new Intent(this, KeyboardLauncherActivity.class); // New task needed because don't launch from an Activity context - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); break; case KEY_LOCK: From 3f90276ce01fcd10ea8b74cc6709578c8c6c7b8d Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 22 Feb 2019 16:52:02 +0100 Subject: [PATCH 063/289] Refactor Timeout --- .../java/com/kunzisoft/keepass/activities/GroupActivity.java | 2 +- .../com/kunzisoft/keepass/activities/lock/LockingActivity.kt | 1 + app/src/main/java/com/kunzisoft/keepass/app/App.java | 2 +- .../kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt | 3 +-- .../main/java/com/kunzisoft/keepass/database/Database.java | 2 +- .../keepass/magikeyboard/KeyboardLauncherActivity.kt | 3 +-- .../java/com/kunzisoft/keepass/password/PasswordActivity.java | 4 ++-- .../main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt | 1 - 8 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 3539c339b..acdba1965 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -1208,7 +1208,7 @@ public class GroupActivity extends LockingActivity // Else lock if needed else { if (PreferencesUtil.isLockDatabaseWhenBackButtonOnRootClicked(this)) { - App.getDB().clear(getApplicationContext()); + App.getDB().closeAndClear(getApplicationContext()); super.onBackPressed(); } else { moveTaskToBack(true); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index 5601d9433..e6ebf039f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -180,6 +180,7 @@ fun Activity.lock() { (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).apply { cancelAll() } + App.getDB().closeAndClear(applicationContext) setResult(LockingActivity.RESULT_EXIT_LOCK) finish() } diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.java b/app/src/main/java/com/kunzisoft/keepass/app/App.java index abec3ebf1..608766c02 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.java +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.java @@ -67,7 +67,7 @@ public class App extends MultiDexApplication { @Override public void onTerminate() { if ( db != null ) { - db.clear(getApplicationContext()); + db.closeAndClear(getApplicationContext()); } super.onTerminate(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt index d04a28fd6..a51623195 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt @@ -38,10 +38,9 @@ class AutoFillLauncherActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE) - TimeoutHelper.checkTime(this) val assistStructure = AutofillHelper.retrieveAssistStructure(intent) if (assistStructure != null) { - if (App.getDB().loaded) + if (App.getDB().loaded && TimeoutHelper.checkTime(this)) GroupActivity.launchForAutofillResult(this, assistStructure, true) else { FileSelectActivity.launchForAutofillResult(this, assistStructure) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/Database.java index 41ec09810..6addc45c3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/Database.java @@ -299,7 +299,7 @@ public class Database { } // TODO Clear database when lock broadcast is receive in backstage - public void clear(Context context) { + public void closeAndClear(Context context) { drawFactory.clearCache(); // Delete the cache of the database if present if (pwDatabase != null) diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt index 0262d27cc..8b7e25216 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt @@ -14,8 +14,7 @@ class KeyboardLauncherActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - TimeoutHelper.checkTime(this) - if (App.getDB().loaded) + if (App.getDB().loaded && TimeoutHelper.checkTime(this)) GroupActivity.launchForKeyboardSelection(this, true) else { // Pass extra to get entry diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 04170ec9a..73167dfff 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -881,7 +881,7 @@ public class PasswordActivity extends StylishActivity private void loadDatabase(String password, Uri keyfile) { // Clear before we load Database database = App.getDB(); - database.clear(getApplicationContext()); + database.closeAndClear(getApplicationContext()); // Show the progress dialog Handler handler = new Handler(); @@ -1053,7 +1053,7 @@ public class PasswordActivity extends StylishActivity case LockingActivity.RESULT_EXIT_LOCK: case Activity.RESULT_CANCELED: setEmptyViews(); - App.getDB().clear(getApplicationContext()); + App.getDB().closeAndClear(getApplicationContext()); break; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index 903ce1a8c..d94397a5c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -118,7 +118,6 @@ object TimeoutHelper { val diff = currentTime - timeoutBackup if (diff >= appTimeout) { // We have timed out - App.getDB().loaded = false timeoutAction?.invoke() return false } From b40d2f3b9a5a7922974e1a8ee212f5d88911a969 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 22 Feb 2019 17:29:17 +0100 Subject: [PATCH 064/289] Fix keyboard settings --- .../keepass/magikeyboard/MagikIME.java | 76 +++++++++++-------- .../keepass/settings/MagikIMESettings.java | 4 +- .../settings/NestedSettingsFragment.java | 3 +- .../keepass/settings/PreferencesUtil.java | 12 +++ 4 files changed, 61 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java index 7ab991bf4..c5fe63a68 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/MagikIME.java @@ -22,12 +22,10 @@ package com.kunzisoft.keepass.magikeyboard; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; import android.media.AudioManager; -import android.support.v7.preference.PreferenceManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; @@ -48,6 +46,7 @@ import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter; import com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver; import com.kunzisoft.keepass.magikeyboard.view.MagikeyboardView; import com.kunzisoft.keepass.model.Entry; +import com.kunzisoft.keepass.settings.PreferencesUtil; import static com.kunzisoft.keepass.magikeyboard.receiver.LockBroadcastReceiver.LOCK_ACTION; @@ -124,16 +123,6 @@ public class MagikIME extends InputMethodService View closeView = popupFieldsView.findViewById(R.id.keyboard_popup_close); closeView.setOnClickListener(v -> popupCustomKeys.dismiss()); - // Define preferences - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - keyboardView.setHapticFeedbackEnabled( - sharedPreferences.getBoolean( - getString(R.string.keyboard_key_vibrate_key), - getResources().getBoolean(R.bool.keyboard_key_vibrate_default))); - playSoundDuringCLick = sharedPreferences.getBoolean( - getString(R.string.keyboard_key_sound_key), - getResources().getBoolean(R.bool.keyboard_key_sound_default)); - return keyboardView; } @@ -148,6 +137,10 @@ public class MagikIME extends InputMethodService keyboardView.setKeyboard(keyboard); } } + + // Define preferences + keyboardView.setHapticFeedbackEnabled(PreferencesUtil.enableKeyboardVibration(this)); + playSoundDuringCLick = PreferencesUtil.enableKeyboardSound(this); } } @@ -171,12 +164,35 @@ public class MagikIME extends InputMethodService entryKey = null; } + private void playVibration(int keyCode){ + switch(keyCode){ + case Keyboard.KEYCODE_DELETE: + break; + default: keyboardView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); + } + } + + private void playClick(int keyCode){ + AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE); + if (am != null) + switch(keyCode){ + case Keyboard.KEYCODE_DONE: + case 10: + am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN); + break; + case Keyboard.KEYCODE_DELETE: + am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE); + break; + default: am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD); + } + } + @Override public void onKey(int primaryCode, int[] keyCodes) { InputConnection inputConnection = getCurrentInputConnection(); // Vibrate - keyboardView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); + playVibration(primaryCode); // Play a sound if (playSoundDuringCLick) playClick(primaryCode); @@ -246,10 +262,25 @@ public class MagikIME extends InputMethodService } @Override - public void onPress(int primaryCode) {} + public void onPress(int primaryCode) { + AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE); + if (am != null) + switch(primaryCode){ + case Keyboard.KEYCODE_DELETE: + keyboardView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + break; + default: + } + } @Override - public void onRelease(int primaryCode) {} + public void onRelease(int primaryCode) { + switch(primaryCode){ + case Keyboard.KEYCODE_DELETE: + break; + default: + } + } @Override public void onText(CharSequence text) {} @@ -266,21 +297,6 @@ public class MagikIME extends InputMethodService @Override public void swipeUp() {} - private void playClick(int keyCode){ - AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE); - if (am != null) - switch(keyCode){ - case Keyboard.KEYCODE_DONE: - case 10: - am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN); - break; - case Keyboard.KEYCODE_DELETE: - am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE); - break; - default: am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD); - } - } - private void dismissCustomKeys() { if (popupCustomKeys != null) popupCustomKeys.dismiss(); diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java index 72a65ac6c..fadc84980 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java @@ -24,9 +24,9 @@ import android.support.v7.widget.Toolbar; import android.view.MenuItem; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.lock.LockingActivity; +import com.kunzisoft.keepass.stylish.StylishActivity; -public class MagikIMESettings extends LockingActivity { +public class MagikIMESettings extends StylishActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index 61f219cd7..425f336b6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -310,8 +310,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat Preference keyboardSubPreference = findPreference(getString(R.string.magic_keyboard_preference_key)); keyboardSubPreference.setOnPreferenceClickListener(preference -> { - Intent intentKeyboard = new Intent(getContext(), MagikIMESettings.class); - startActivity(intentKeyboard); + startActivity(new Intent(getContext(), MagikIMESettings.class)); return false; }); diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java index 499a664e5..0493ffe9e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java @@ -203,6 +203,18 @@ public class PreferencesUtil { context.getResources().getBoolean(R.bool.keyboard_notification_entry_default)); } + public static boolean enableKeyboardVibration(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean(context.getString(R.string.keyboard_key_vibrate_key), + context.getResources().getBoolean(R.bool.keyboard_key_vibrate_default)); + } + + public static boolean enableKeyboardSound(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean(context.getString(R.string.keyboard_key_sound_key), + context.getResources().getBoolean(R.bool.keyboard_key_sound_default)); + } + /** * All preference keys associated with education */ From 9ac25b0d74c9da0141d2a011abc9b50e5891cb17 Mon Sep 17 00:00:00 2001 From: Casiyre Date: Fri, 22 Feb 2019 02:35:11 +0000 Subject: [PATCH 065/289] Translated using Weblate (English) Currently translated at 100.0% (345 of 345 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 e4d7bc765..57a1788f3 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 @@ -121,7 +120,7 @@ Mask passwords (***) by default About Change master key - Copy of %1$s + Copied %1$s Settings App settings Form filling @@ -274,7 +273,6 @@ Text App Other - Keyboard Magikeyboard Activate a custom keyboard populating your passwords and all identity fields @@ -291,50 +289,38 @@ Fill in your fields using the entry elements. Lock the database. Use default keyboard again. - Magikeyboard Magikeyboard (KeePass DX) Magikeyboard settings - Entry - erase_entry_timeout_key Timeout Timeout to clear the keyboard entry - notification_entry_key Notification information Show a notification when an entry is available true - Entry %1$s available on Magikeyboard %1$s - notification_entry_slide_close_key Clear at closing Clear the keyboard entry when closing the notification true - Appearance - keyboard_theme_key Keyboard theme - Keys keyboard_key_vibrate_key Vibrate on keypress true - key_sound_key Sound on keypress false - Allow no password Enable the \"Open\" button if no password identification is selected Write-protected Open your database read-only by default - Educational screens Highlight the elements to learn how the app works Reset educational screens @@ -371,12 +357,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>, @@ -384,36 +368,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 @@ -426,5 +405,4 @@ Icon pack Icon pack used in the app - - + \ No newline at end of file From 783cbc634e155d5fe0394141112b57c0a6143099 Mon Sep 17 00:00:00 2001 From: Mesut Akcan Date: Fri, 22 Feb 2019 06:26:20 +0000 Subject: [PATCH 066/289] Translated using Weblate (Turkish) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 33 ++++++++------------------ 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 29fc4a86a..6634aa4b2 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,5 +1,6 @@ - -Geri Bildirim + + + Geri Bildirim Ana sayfa KeePass parola yöneticisinin Android uygulaması Kabul et @@ -47,7 +48,7 @@ Yolun doğru olduğundan emin olun. Bir isim girin. Şimdi panoyu temizlemek için kaydırın - %1$s dosyasını panoya kopyalamak için seçin + %1$s dosyasını panoya kopyalamak için seçin Veritabanı anahtarı alınıyor… Veritabanı içeriği deşifre ediliyor… Varsayılan veritabanı olarak kullan @@ -101,7 +102,7 @@ Parola maskesi. Varsayılan (***) Hakkında Ana anahtarı değitir - 1$s kopyası + 1$s kopyalandı Ayarlar Uygulama ayarları Form doldurma @@ -252,7 +253,6 @@ Metin Uygulama Diğer - Klavye Magikeyboard Parolalarınızı ve tüm kimlik alanlarınızı içeren özel bir klavye etkinleştirin @@ -267,28 +267,21 @@ Giriş öğelerini kullanarak alanlarınızı doldurun. Veritabanını kilitle. Varsayılan klavyeyi tekrar kullan. - Magikeyboard Magikeyboard (KeePass DX) Magikeyboard ayarları - Girdi - Zaman aşımı Klavye girişini temizlemek için zaman aşımı - Bildirim bilgisi Bir giriş mevcut olduğunda bir bildirim göster Girdi Magikeyboard\'da %1$s mevcut %1$s - Kapanışta temizle Bildirimi kapatırken klavye girişini temizle Görünüm - Klavye teması - Anahtarlar Tuşa basıldığında titreştir Tuşa basıldığında ses çıkar @@ -296,7 +289,6 @@ Parola tanımlaması seçilmemişse \"Aç\" düğmesini etkinleştir Yazma korumalı Veritabanınızı varsayılan olarak salt okunur açın - Eğitim ekranları Uygulamanın nasıl çalıştığını öğrenmek için öğeleri vurgulayın Eğitim ekranlarını sıfırla @@ -341,33 +333,28 @@ Girdilerin ve grupların nasıl sıralandığını seçin. Katıl Daha fazla özellik ekleyerek istikrarı, güvenliği artırmaya yardımcı olun. - Birçok şifre yönetimi uygulamasından farklı olarak, bu, ad-free, copylefted libre yazılımı şeklindedir ve hangi sürümü kullanıyor olursanız olun, kendi sunucularında kişisel verileri toplamaz. Profesyonel sürümü satın alarak, bu görsel özelliğe erişebilecek ve özellikle topluluk projelerinin gerçekleştirilmesine yardımcı olacaksınız Bu görsel özellik, cömertliğiniz sayesinde kullanılabilir. Özgürlüğümüzü korumak ve daima aktif olmak için katkılarınıza güveniyoruz - Bu özellik geliştirme aşamasındadır ve katkılarınızın yakında kullanıma sunulmasını gerektirir. - Pro sürümünü satın alarak, - Katkıda bulunarak, + + Pro sürümünü satın alarak, + + Katkıda bulunarak, Geliştiricilerin yeni özellikler oluşturmasını ve söz konusu hatalara göre hataları düzeltmesini teşvik ediyorsunuz. Katkınız için çok teşekkür ederim. Bu özelliği çabucak yayınlamak için çok çalışıyoruz. Yeni sürümleri yükleyerek uygulamanızı güncel tutmayı unutmayın. - İndir Katkıda bulun - Rijndael (AES) Twofish ChaCha20 - AES KDF Argon2 - Uygulama teması Uygulamada kullanılan tema Simge paketi Uygulamada kullanılan simge paketi - - + \ No newline at end of file From 953e63f55d87269f4723a6dc3c2df1ccd9ad4328 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 23 Feb 2019 14:39:03 +0100 Subject: [PATCH 067/289] Add selection mode and fix read only --- app/src/main/AndroidManifest.xml | 2 +- .../keepass/activities/EntryActivity.java | 2 +- .../EntrySelectionHelper.kt | 13 ++- .../keepass/activities/GroupActivity.java | 110 +++++++++--------- .../keepass/activities/ListNodesFragment.java | 6 +- .../keepass/activities/ReadOnlyHelper.java | 61 ---------- .../keepass/activities/ReadOnlyHelper.kt | 59 ++++++++++ .../activities/lock/LockingActivity.kt | 13 ++- .../autofill/AutoFillLauncherActivity.kt | 3 +- .../keepass/autofill/AutofillHelper.kt | 2 +- .../fileselect/FileSelectActivity.java | 2 +- .../keepass/magikeyboard/KeyboardHelper.kt | 2 +- .../magikeyboard/KeyboardLauncherActivity.kt | 3 +- .../keepass/password/PasswordActivity.java | 6 +- .../settings/NestedSettingsFragment.java | 6 +- .../res/layout/list_nodes_with_add_button.xml | 23 +++- app/src/main/res/values/strings.xml | 1 + 17 files changed, 170 insertions(+), 144 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/{selection => activities}/EntrySelectionHelper.kt (74%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4f111ac2b..69bdaf58e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -101,7 +101,7 @@ android:name="com.kunzisoft.keepass.activities.GroupActivity" android:configChanges="orientation|keyboardHidden" android:windowSoftInputMode="adjustPan" - android:launchMode="singleTop"> + android:launchMode="singleTask"> Unit, keyboardAction: () -> Unit, @@ -25,7 +34,7 @@ object EntrySelectionHelper { } } if (assistStructure == null) { - if (intent.getBooleanExtra(EXTRA_ENTRY_SELECTION_MODE, false)) { + if (intent.getBooleanExtra(EXTRA_ENTRY_SELECTION_MODE, DEFAULT_ENTRY_SELECTION_MODE)) { intent.removeExtra(EXTRA_ENTRY_SELECTION_MODE) keyboardAction.invoke() } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index acdba1965..eafa8fc49 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -86,7 +86,6 @@ import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.magikeyboard.MagikIME; import com.kunzisoft.keepass.password.AssignPasswordHelper; -import com.kunzisoft.keepass.selection.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; import com.kunzisoft.keepass.tasks.UIToastTask; @@ -97,8 +96,6 @@ import com.kunzisoft.keepass.view.AddNodeButtonView; import net.cachapa.expandablelayout.ExpandableLayout; -import static com.kunzisoft.keepass.activities.ReadOnlyHelper.READ_ONLY_DEFAULT; - public class GroupActivity extends LockingActivity implements GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener, @@ -122,6 +119,7 @@ public class GroupActivity extends LockingActivity private ExpandableLayout toolbarPasteExpandableLayout; private Toolbar toolbarPaste; private ImageView iconView; + private TextView modeTitleView; private AddNodeButtonView addNodeButtonView; private TextView groupNameView; @@ -147,7 +145,7 @@ public class GroupActivity extends LockingActivity if (group != null) { intent.putExtra(GROUP_ID_KEY, group.getId()); } - ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly); + ReadOnlyHelper.INSTANCE.putReadOnlyInIntent(intent, readOnly); intentBuildLauncher.launchActivity(intent); } } @@ -159,7 +157,7 @@ public class GroupActivity extends LockingActivity */ public static void launch(Activity activity) { - launch(activity, READ_ONLY_DEFAULT); + launch(activity, PreferencesUtil.enableReadOnlyDatabase(activity)); } public static void launch(Activity activity, boolean readOnly) { @@ -178,15 +176,11 @@ public class GroupActivity extends LockingActivity * Keyboard Launch * ------------------------- */ + // TODO implement pre search to directly open the direct group public static void launchForKeyboardSelection(Activity activity, boolean readOnly) { - launchForKeyboardSelection(activity, null, readOnly); - } - - public static void launchForKeyboardSelection(Activity activity, PwGroup group, boolean readOnly) { - // TODO implement pre search to directly open the direct group TimeoutHelper.INSTANCE.recordTime(activity); - buildAndLaunchIntent(activity, group, readOnly, + buildAndLaunchIntent(activity, null, readOnly, (intent) -> KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent)); } @@ -195,19 +189,14 @@ public class GroupActivity extends LockingActivity * Autofill Launch * ------------------------- */ + // TODO implement pre search to directly open the direct group @RequiresApi(api = Build.VERSION_CODES.O) public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure, boolean readOnly) { - launchForAutofillResult(activity, null, assistStructure, readOnly); - } - - @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity activity, PwGroup group, @NonNull AssistStructure assistStructure, boolean readOnly) { - // TODO implement pre search to directly open the direct group TimeoutHelper.INSTANCE.recordTime(activity); - buildAndLaunchIntent(activity, group, readOnly, + buildAndLaunchIntent(activity, null, readOnly, (intent) -> AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, intent, assistStructure)); - } + } @Override protected void onCreate(Bundle savedInstanceState) { @@ -230,15 +219,11 @@ public class GroupActivity extends LockingActivity groupNameView = findViewById(R.id.group_name); toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout); toolbarPaste = findViewById(R.id.toolbar_paste); + modeTitleView = findViewById(R.id.mode_title_view); // Focus view to reinitialize timeout resetAppTimeoutWhenViewFocusedOrChanged(addNodeButtonView); - invalidateOptionsMenu(); - - // Get arg from intent or instance state - setReadOnly(ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, getIntent())); - // Retrieve elements after an orientation change if (savedInstanceState != null) { if (savedInstanceState.containsKey(OLD_GROUP_TO_UPDATE_KEY)) @@ -380,7 +365,6 @@ public class GroupActivity extends LockingActivity outState.putParcelable(NODE_TO_COPY_KEY, nodeToCopy); if (nodeToMove != null) outState.putParcelable(NODE_TO_MOVE_KEY, nodeToMove); - ReadOnlyHelper.onSaveInstanceState(outState, getReadOnly()); super.onSaveInstanceState(outState); } @@ -459,6 +443,13 @@ public class GroupActivity extends LockingActivity } } + // Show selection mode message if needed + if (getSelectionMode()) { + modeTitleView.setVisibility(View.VISIBLE); + } else { + modeTitleView.setVisibility(View.GONE); + } + // Show button if allowed if (addNodeButtonView != null) { @@ -513,6 +504,8 @@ public class GroupActivity extends LockingActivity GroupActivity.this, KeyboardEntryNotificationService.class)); } + // Consume the selection mode + EntrySelectionHelper.INSTANCE.removeEntrySelectionModeFromIntent(getIntent()); moveTaskToBack(true); return null; }, @@ -856,41 +849,42 @@ public class GroupActivity extends LockingActivity @Override public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.search, menu); - if (!getReadOnly()) - inflater.inflate(R.menu.database_master_key, menu); - inflater.inflate(R.menu.database_lock, menu); + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.search, menu); + inflater.inflate(R.menu.database_lock, menu); + if (!getReadOnly()) + inflater.inflate(R.menu.database_master_key, menu); + if (!getSelectionMode()) { + inflater.inflate(R.menu.default_menu, menu); + MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); + } - // Get the SearchView and set the searchable configuration - SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); - assert searchManager != null; + // Get the SearchView and set the searchable configuration + SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); + assert searchManager != null; - MenuItem searchItem = menu.findItem(R.id.menu_search); - SearchView searchView = null; - if (searchItem != null) { - searchView = (SearchView) searchItem.getActionView(); - } - if (searchView != null) { - searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, GroupActivity.class))); - searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default - searchView.setSuggestionsAdapter(searchSuggestionAdapter); - searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { - @Override - public boolean onSuggestionClick(int position) { - onNodeClick(searchSuggestionAdapter.getEntryFromPosition(position)); - return true; - } + MenuItem searchItem = menu.findItem(R.id.menu_search); + SearchView searchView = null; + if (searchItem != null) { + searchView = (SearchView) searchItem.getActionView(); + } + if (searchView != null) { + searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, GroupActivity.class))); + searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default + searchView.setSuggestionsAdapter(searchSuggestionAdapter); + searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { + @Override + public boolean onSuggestionClick(int position) { + onNodeClick(searchSuggestionAdapter.getEntryFromPosition(position)); + return true; + } - @Override - public boolean onSuggestionSelect(int position) { - return true; - } - }); - } - - MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); - inflater.inflate(R.menu.default_menu, menu); + @Override + public boolean onSuggestionSelect(int position) { + return true; + } + }); + } super.onCreateOptionsMenu(menu); @@ -900,7 +894,7 @@ public class GroupActivity extends LockingActivity return true; } - @Override + @Override public void startActivity(Intent intent) { // Get the intent, verify the action and get the query diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java index 4fcde82a0..c7d8db722 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java @@ -58,7 +58,7 @@ public class ListNodesFragment extends StylishFragment implements bundle.putParcelable(GROUP_KEY, group); } bundle.putBoolean(IS_SEARCH, isASearch); - ReadOnlyHelper.putReadOnlyInBundle(bundle, readOnly); + ReadOnlyHelper.INSTANCE.putReadOnlyInBundle(bundle, readOnly); ListNodesFragment listNodesFragment = new ListNodesFragment(); listNodesFragment.setArguments(bundle); return listNodesFragment; @@ -99,7 +99,7 @@ public class ListNodesFragment extends StylishFragment implements if ( getActivity() != null ) { setHasOptionsMenu(true); - readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, getArguments()); + readOnly = ReadOnlyHelper.INSTANCE.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, getArguments()); if (getArguments() != null) { // Contains all the group in element @@ -127,7 +127,7 @@ public class ListNodesFragment extends StylishFragment implements @Override public void onSaveInstanceState(@NonNull Bundle outState) { - ReadOnlyHelper.onSaveInstanceState(outState, readOnly); + ReadOnlyHelper.INSTANCE.onSaveInstanceState(outState, readOnly); super.onSaveInstanceState(outState); } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.java b/app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.java deleted file mode 100644 index 78164c69a..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.kunzisoft.keepass.activities; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import com.kunzisoft.keepass.settings.PreferencesUtil; - -public class ReadOnlyHelper { - - public static final String READ_ONLY_KEY = "READ_ONLY_KEY"; - - public static final boolean READ_ONLY_DEFAULT = false; - - public static boolean retrieveReadOnlyFromInstanceStateOrPreference(Context context, Bundle savedInstanceState) { - boolean readOnly; - if (savedInstanceState != null - && savedInstanceState.containsKey(READ_ONLY_KEY)) { - readOnly = savedInstanceState.getBoolean(READ_ONLY_KEY); - } else { - readOnly = PreferencesUtil.enableReadOnlyDatabase(context); - } - return readOnly; - } - - public static boolean retrieveReadOnlyFromInstanceStateOrArguments(Bundle savedInstanceState, Bundle arguments) { - boolean readOnly = READ_ONLY_DEFAULT; - if (savedInstanceState != null - && savedInstanceState.containsKey(READ_ONLY_KEY)) { - readOnly = savedInstanceState.getBoolean(READ_ONLY_KEY); - } else if (arguments != null - && arguments.containsKey(READ_ONLY_KEY)) { - readOnly = arguments.getBoolean(READ_ONLY_KEY); - } - return readOnly; - } - - public static boolean retrieveReadOnlyFromInstanceStateOrIntent(Bundle savedInstanceState, Intent intent) { - boolean readOnly = READ_ONLY_DEFAULT; - if (savedInstanceState != null - && savedInstanceState.containsKey(READ_ONLY_KEY)) { - readOnly = savedInstanceState.getBoolean(READ_ONLY_KEY); - } else { - if (intent != null) - readOnly = intent.getBooleanExtra(READ_ONLY_KEY, READ_ONLY_DEFAULT); - } - return readOnly; - } - - public static void putReadOnlyInIntent(Intent intent, boolean readOnly) { - intent.putExtra(READ_ONLY_KEY, readOnly); - } - - public static void putReadOnlyInBundle(Bundle bundle, boolean readOnly) { - bundle.putBoolean(READ_ONLY_KEY, readOnly); - } - - public static void onSaveInstanceState(Bundle outState, boolean readOnly) { - outState.putBoolean(READ_ONLY_KEY, readOnly); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.kt new file mode 100644 index 000000000..decc14837 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ReadOnlyHelper.kt @@ -0,0 +1,59 @@ +package com.kunzisoft.keepass.activities + +import android.content.Context +import android.content.Intent +import android.os.Bundle + +import com.kunzisoft.keepass.settings.PreferencesUtil + +object ReadOnlyHelper { + + private const val READ_ONLY_KEY = "READ_ONLY_KEY" + + const val READ_ONLY_DEFAULT = false + + fun retrieveReadOnlyFromIntent(intent: Intent): Boolean { + return intent.getBooleanExtra(READ_ONLY_KEY, READ_ONLY_DEFAULT) + } + + fun retrieveReadOnlyFromInstanceStateOrPreference(context: Context, savedInstanceState: Bundle?): Boolean { + return if (savedInstanceState != null && savedInstanceState.containsKey(READ_ONLY_KEY)) { + savedInstanceState.getBoolean(READ_ONLY_KEY) + } else { + PreferencesUtil.enableReadOnlyDatabase(context) + } + } + + fun retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState: Bundle?, arguments: Bundle?): Boolean { + var readOnly = READ_ONLY_DEFAULT + if (savedInstanceState != null && savedInstanceState.containsKey(READ_ONLY_KEY)) { + readOnly = savedInstanceState.getBoolean(READ_ONLY_KEY) + } else if (arguments != null && arguments.containsKey(READ_ONLY_KEY)) { + readOnly = arguments.getBoolean(READ_ONLY_KEY) + } + return readOnly + } + + fun retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState: Bundle?, intent: Intent?): Boolean { + var readOnly = READ_ONLY_DEFAULT + if (savedInstanceState != null && savedInstanceState.containsKey(READ_ONLY_KEY)) { + readOnly = savedInstanceState.getBoolean(READ_ONLY_KEY) + } else { + if (intent != null) + readOnly = intent.getBooleanExtra(READ_ONLY_KEY, READ_ONLY_DEFAULT) + } + return readOnly + } + + fun putReadOnlyInIntent(intent: Intent, readOnly: Boolean) { + intent.putExtra(READ_ONLY_KEY, readOnly) + } + + fun putReadOnlyInBundle(bundle: Bundle, readOnly: Boolean) { + bundle.putBoolean(READ_ONLY_KEY, readOnly) + } + + fun onSaveInstanceState(outState: Bundle, readOnly: Boolean) { + outState.putBoolean(READ_ONLY_KEY, readOnly) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index e6ebf039f..c91d43a22 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -28,6 +28,7 @@ import android.content.IntentFilter import android.os.Bundle import android.util.Log import android.view.View +import com.kunzisoft.keepass.activities.EntrySelectionHelper import com.kunzisoft.keepass.activities.ReadOnlyHelper import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.settings.PreferencesUtil @@ -51,7 +52,12 @@ abstract class LockingActivity : StylishActivity() { private var lockReceiver: LockReceiver? = null private var exitLock: Boolean = false + // Force readOnly if Entry Selection mode protected var readOnly: Boolean = false + get() { + return field || selectionMode + } + protected var selectionMode: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -75,8 +81,6 @@ abstract class LockingActivity : StylishActivity() { } exitLock = false - - readOnly = false readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, intent) } @@ -93,6 +97,9 @@ abstract class LockingActivity : StylishActivity() { override fun onResume() { super.onResume() + // To refresh when back to normal workflow from selection workflow + selectionMode = EntrySelectionHelper.retrieveEntrySelectionModeFromIntent(intent) + if (timeoutEnable) { // End activity if database not loaded if (!App.getDB().loaded) { @@ -108,6 +115,8 @@ abstract class LockingActivity : StylishActivity() { if (!exitLock) TimeoutHelper.recordTime(this) } + + invalidateOptionsMenu() } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt index a51623195..22919c23e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt @@ -31,6 +31,7 @@ import android.support.v7.app.AppCompatActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.fileselect.FileSelectActivity +import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper @RequiresApi(api = Build.VERSION_CODES.O) @@ -41,7 +42,7 @@ class AutoFillLauncherActivity : AppCompatActivity() { val assistStructure = AutofillHelper.retrieveAssistStructure(intent) if (assistStructure != null) { if (App.getDB().loaded && TimeoutHelper.checkTime(this)) - GroupActivity.launchForAutofillResult(this, assistStructure, true) + GroupActivity.launchForAutofillResult(this, assistStructure, PreferencesUtil.enableReadOnlyDatabase(this)) else { FileSelectActivity.launchForAutofillResult(this, assistStructure) } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt index 9048a4558..89b119c27 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -32,8 +32,8 @@ import android.view.autofill.AutofillManager import android.view.autofill.AutofillValue import android.widget.RemoteViews import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.EntrySelectionHelper import com.kunzisoft.keepass.database.PwEntry -import com.kunzisoft.keepass.selection.EntrySelectionHelper import java.util.* diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java index c9abd1741..7466d0148 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java @@ -58,7 +58,7 @@ import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.password.AssignPasswordHelper; import com.kunzisoft.keepass.password.PasswordActivity; -import com.kunzisoft.keepass.selection.EntrySelectionHelper; +import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment; diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt index b6b9b37bf..7830d8837 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardHelper.kt @@ -2,7 +2,7 @@ package com.kunzisoft.keepass.magikeyboard import android.app.Activity import android.content.Intent -import com.kunzisoft.keepass.selection.EntrySelectionHelper +import com.kunzisoft.keepass.activities.EntrySelectionHelper object KeyboardHelper { diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt index 8b7e25216..2d3b87308 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt @@ -5,6 +5,7 @@ import android.support.v7.app.AppCompatActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.fileselect.FileSelectActivity +import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper class KeyboardLauncherActivity : AppCompatActivity() { @@ -15,7 +16,7 @@ class KeyboardLauncherActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { if (App.getDB().loaded && TimeoutHelper.checkTime(this)) - GroupActivity.launchForKeyboardSelection(this, true) + GroupActivity.launchForKeyboardSelection(this, PreferencesUtil.enableReadOnlyDatabase(this)) else { // Pass extra to get entry FileSelectActivity.launchForKeyboardSelection(this) diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 73167dfff..71a70094e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -71,7 +71,7 @@ import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; -import com.kunzisoft.keepass.selection.EntrySelectionHelper; +import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment; @@ -243,7 +243,7 @@ public class PasswordActivity extends StylishActivity checkboxKeyfileView = findViewById(R.id.keyfile_checkox); checkboxDefaultDatabaseView = findViewById(R.id.default_database); - readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState); + readOnly = ReadOnlyHelper.INSTANCE.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState); View browseView = findViewById(R.id.browse_button); keyFileHelper = new KeyFileHelper(PasswordActivity.this); @@ -334,7 +334,7 @@ public class PasswordActivity extends StylishActivity @Override protected void onSaveInstanceState(Bundle outState) { - ReadOnlyHelper.onSaveInstanceState(outState, readOnly); + ReadOnlyHelper.INSTANCE.onSaveInstanceState(outState, readOnly); super.onSaveInstanceState(outState); } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index 425f336b6..8d2b37910 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -90,7 +90,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat // supply arguments to bundle. Bundle args = new Bundle(); args.putInt(TAG_KEY, key.ordinal()); - ReadOnlyHelper.putReadOnlyInBundle(args, databaseReadOnly); + ReadOnlyHelper.INSTANCE.putReadOnlyInBundle(args, databaseReadOnly); fragment.setArguments(args); return fragment; } @@ -124,7 +124,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat key = getArguments().getInt(TAG_KEY); database = App.getDB(); - databaseReadOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, getArguments()); + databaseReadOnly = ReadOnlyHelper.INSTANCE.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, getArguments()); databaseReadOnly = database.isReadOnly() || databaseReadOnly; // Load the preferences from an XML resource @@ -552,7 +552,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat @Override public void onSaveInstanceState(Bundle outState) { - ReadOnlyHelper.onSaveInstanceState(outState, databaseReadOnly); + ReadOnlyHelper.INSTANCE.onSaveInstanceState(outState, databaseReadOnly); super.onSaveInstanceState(outState); } diff --git a/app/src/main/res/layout/list_nodes_with_add_button.xml b/app/src/main/res/layout/list_nodes_with_add_button.xml index 99c89f152..0cba7aefc 100644 --- a/app/src/main/res/layout/list_nodes_with_add_button.xml +++ b/app/src/main/res/layout/list_nodes_with_add_button.xml @@ -98,13 +98,26 @@ - + android:layout_below="@+id/toolbar"> + + + Write-protected KeePass DX needs write permission in order to change anything in your database. Starting with Android KitKat, some devices no longer allow apps to write to the SD card. + Selection mode Recent file history Remember recent filenames Remembers the location of databases keyfiles From 529197fb7dbe6f5e2132b53362fb094c749b3ced Mon Sep 17 00:00:00 2001 From: jan madsen Date: Mon, 25 Feb 2019 10:17:36 +0000 Subject: [PATCH 068/289] Translated using Weblate (Danish) Currently translated at 98.6% (340 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/ --- app/src/main/res/values-da/strings.xml | 62 ++++++++++---------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 7788ebe28..faa8d114a 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -1,4 +1,4 @@ - + - +--> Tilbagemelding Hjemmeside Android implementering af KeePass password manager @@ -31,7 +30,7 @@ Inaktivitet før programmet bliver er låst App Indstillinger - Vis ikke igen + Vis ikke igen Parenteser Installer OpenIntents Fil Manager for at gennemse filer Annuller @@ -160,24 +159,23 @@ Angiv en adgangskode og/eller en nøglefil til at låse databasen op. \n \nHusk at gemme en kopi af .kdbx filen i et sikkert sted efter hver ændring. - - 5 sekunder - 10 sekunder - 20 sekunder - 30 sekunder - 1 minut - 5 minutter - 15 minutter - 30 minutter - Aldrig + 5 sekunder + 10 sekunder + 20 sekunder + 30 sekunder + 1 minut + 5 minutter + 15 minutter + 30 minutter + Aldrig - Lille - Mellem - Stor + Lille + Mellem + Stor -Rediger post + Rediger post Tilføj streng Kryptering Nøgleafledningsfunktion @@ -191,7 +189,7 @@ Kunne ikke finde filen. Prøv at åbne den fra filhåndtering. Vis brugernavne Vis brugernavne i postlister - Kopi af %1$s + Kopieret %1$s Formularudfyldning Kopier Flyt @@ -293,7 +291,6 @@ Tekst Program Øvrige - Tastatur Magikeyboard Aktiver et brugerdefineret tastatur, der udfylder adgangskoder og alle identitetsfelter @@ -310,12 +307,10 @@ Udfyld felterne ved hjælp af elementerne i posten. Lås databasen. Brug standard tastaturet igen. - Tillad ingen adgangskode Aktiver knappen \"Åbn\", hvis der ikke er valgt en adgangskodeidentifikation Skrivebeskyttet Åbn database skrivebeskyttet som standard - Hjælpeskærme Fremhæver elementer for at lære, hvordan programmet fungerer Nulstil hjælpeskærme @@ -358,12 +353,12 @@ Vælg hvordan poster og grupper er sorteret. Deltag Bidrag til at øge stabiliteten, sikkerheden og med at tilføje flere funktioner. - I modsætning til andre programmer til adgangskodeadministration er denne annoncefri , copyleft fri software", og indsamler ikke personlige data, uanset hvilken version der bruges." - Ved at købe pro-versionen, er der adgang til visuel funktionen, og det vil især hjælpe gennemførelsen af lokale projekter. + Ved at købe pro-versionen, er der adgang til visuel funktionen, og det vil især hjælpe gennemførelsen af lokale projekter. + Denne visuelle funktion er tilgængelige takket være bidrag. - For at bevare uafhængighed og altid at være aktiv, regner vi medbidrag. - + For at bevare uafhængighed og altid at være aktiv, regner vi medbidrag. + Funktionen er under udvikling, og det kræver bidrag, for snart at være tilgængelig. Ved at købe pro versionen, Ved at bidrage, @@ -371,43 +366,32 @@ Tak for bidrag. Vi arbejder hårdt på hurtigt at frigive denne funktion. Glem ikke at holde appen opdateret ved at installere nye versioner. - Hent Bidrag - ChaCha20 - AES KDF Argon2 - Tema Tema, der bruges i programmet Ikonpakke "Ikonpakke, der anvendes " - -Magikeyboard + Magikeyboard Magikeyboard (KeePass DX) Magikeyboard indstillinger - Post - Timeout Meddelsesinformation 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 Udseende - Tastaturtema - Taster Vibrer ved tastetryk Lyd ved tastetryk Build %1$s Timeout for at rydde indtastning - - + \ No newline at end of file From 6f6b53ff7309c88f2fe99911e03a45c7216c6166 Mon Sep 17 00:00:00 2001 From: abvgeej Date: Sun, 24 Feb 2019 21:52:03 +0000 Subject: [PATCH 069/289] Translated using Weblate (Russian) Currently translated at 64.1% (221 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/ --- app/src/main/res/values-ru/strings.xml | 110 ++++++++++++------------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 443385c4a..3b46fe79b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,4 +1,4 @@ - + - - Отзывы: - Сайт: - KeePass DX является Android-версией программы управления паролями KeePass. +--> + Обратная связь + Сайт + Android-версия программы управления паролями KeePass Принять Новая запись Новая группа @@ -32,14 +31,14 @@ Настройки KeePass DX Не показывать снова {[(Скобки)]} - Для функции обзора файлов необходим OI File Manager. Нажмите ниже, чтобы установить. Из-за некоторых проблем с менеджером файлов обзор может работать некорректно при первом запуске. + Для обзора файлов установите OpenIntents File Manager Отмена Буфер обмена очищен Ошибка буфера обмена - На ряде устройств Samsung буфер обмена содержит ошибку, блокирующую копирование данных из приложений. Подробнее: + Некоторые устройства Samsung не дают приложению использовать буфер обмена. Не удалось очистить буфер обмена Задержка очистки буфера обмена - Задержка очистки буфера обмена после копирования логина или пароля + Продолжительность хранения в буфере обмена Выберите %1$s для копирования в буфер обмена Создание ключа базы… База @@ -47,10 +46,10 @@ База по умолчанию Цифры KeePass DX \u00A9 %1$d Kunzisoft Программа предоставляется БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ. Распространяется свободно по лицензии GPL v3 или новее. - Выбрать существующую базу + Открыть существующую базу Доступ Отмена - Комментарии + Заметки Подтверждение Создано Истекает @@ -63,24 +62,24 @@ Ссылка Логин Потоковый шифр RC4 не поддерживается. - Не удалось перейти по указанному адресу. + Не удалось обработать указанный URI в KeePass DX. Не удалось создать родительскую папку. Такой файл уже существует. Не удалось открыть ссылку. Введите имя файла. Не удалось создать файл: Неверный формат базы или неправильный мастер-ключ. - Путь указан неверно. + Убедитесь что путь указан верно. Введите название. - Необходим файл ключа. + Выберите файл ключа. Недостаточно памяти для работы с базой. - Выберите один или несколько типов символов + Выберите один или несколько типов символов. Пароли не совпадают. Введите число. Предельное значение 2147483648. Каждое поле должно иметь название. Введите название. - Поле \"Длина\" должно быть положительным целым числом + Поле \"Длина\" должно быть положительным целым числом. Название поля Значение поля Файл не найден. @@ -98,7 +97,7 @@ F-Droid Неверный пароль или файл ключа. Неверный алгоритм. - Формат базы не определён. + Не удалось определить формат базы. Файл ключа не найден. Файл ключа пуст. Длина пароля @@ -125,7 +124,7 @@ Никогда Совпадения не найдены Не удалось открыть ссылку. - Недавно открытые: + Недавно открытые Не искать в резервных копиях Не искать в Резервировании (.kdb) Создание новой базы… @@ -155,40 +154,38 @@ _Подчёркивание_ Неподдерживаемая версия базы. ЗАГЛАВНЫЕ - Storage Access Framework для обзора файлов (KitKat+) - Обзор через SAF + Использовать Storage Access Framework для обзора файлов (KitKat и выше) + Storage access framework Внимание - Пароль содержит символы вне кодировки Latin1, поддерживаемой форматом .kdb! ВСЕ не-Latin1 символы будут преобразованы в одинаковый символ. Рекомендуется изменить пароль. + Избегайте использования в пароле символов вне кодировки Latin-1 в .kbd файлах, так как эти символы будут преобразованы в одинаковый символ. Карта помять в режиме только для чтения. Изменения не будут сохранены. Карта памяти не подключена. Работа с базой невозможна. Версия %1$s - Отпечатки пальцев поддерживаются, но не настроены на устройстве + Отпечатки пальцев поддерживаются, но не настроены. Ожидание отпечатка пальца Зашифрованный пароль сохранён Неверный ключ отпечатка пальца. Восстановите пароль. Проблема с отпечатком пальца : %1$s Используйте отпечаток пальца, чтобы сохранить пароль Для этой базы пароль ещё не сохранён - Введите пароль и/или файл ключа, чтобы разблокировать базу. \n \nНе забудьте сохранить копию .kdbx файла в безопасном месте после каждого изменения. - - 5 секунд - 10 секунд - 20 секунд - 30 секунд - 1 минута - 5 минут - 15 минут - 30 минут - Никогда + 5 секунд + 10 секунд + 20 секунд + 30 секунд + 1 минута + 5 минут + 15 минут + 30 минут + Никогда - Мелкий - Обычный - Крупный + Мелкий + Обычный + Крупный Шифрование Функция формирования ключа @@ -196,8 +193,8 @@ "Сервис автозаполнения не может быть включен." Копия %1$s Заполнение формы - Алгоритм для шифрования всей базы данных. (Пароли, логины, заметки и все другие данные в базе данных будут зашифрованы указанным алгоритмом) - При генерации ключа для алгоритма шифрования, сжатый мастер-ключ (SHA-256) преобразуется при помощи функции формирования ключа со случайной солью. + Алгоритм шифрования базы для всех данных. + При генерации ключа для алгоритма шифрования, мастер-ключ преобразуется при помощи функции формирования ключа со случайной солью. Использование памяти Количество памяти (в байтах), которое будет использоваться функцией формирования ключа. Уровень параллелизма @@ -205,16 +202,15 @@ Сортировка По возрастанию ↓ Логину - Дате создания - Дате изменения + Создание + Правка Дате использования - -Редактировать запись + Редактировать запись Разрешить Смахните, чтобы очистить буфер обмена сейчас - Невозможно загрузить базу + Невозможно загрузить базу. Невозможно загрузить ключ. Попробуйте уменьшить размер памяти, используемой функцией формирования ключа (KDF). - Невозможно переместить группу в саму себя. + Нельзя переместить группу в саму себя. Показывать логин Показывать логин в списке записей Копировать @@ -268,7 +264,7 @@ Название файла Путь Установить мастер-ключ - Создать файл KeePass + Создать новую базу Байт Путь к файлу Показывать полный путь к файлу @@ -293,7 +289,6 @@ Отображение текста Отображение приложения Прочее - Клавиатура Magikeyboard Активировать пользовательскую клавиатуру для простого заполнения паролей и всех ваших идентификаторов @@ -308,12 +303,10 @@ Заполните поля, используя элементы записи. Заблокируйте базу. Вернутесь к основной клавиатуре. - Разрешить без пароля Включить кнопку открыть, если не выбрана идентификация паролем Только чтение По умолчанию открывать базу только для чтения - Экраны обучения Выделять элементы, чтобы узнать, как работает приложение Сбросить экраны обучения @@ -356,31 +349,30 @@ Сортируйте записи и группы по определенным параметрам. Участвуйте Примите участие в проекте для повышения стабильности, безопасности и добавления новых возможностей. - В отличие от многих приложений управления паролями, это без рекламы, свободное программное обеспечение (copyleft) и не хранит ваши личные данные на своих серверах, даже в бесплатной версии. - При покупке Pro версии, вы будете иметь доступ к этим визуальным функциям и особенно поможете реализации общественных проектов. + При покупке Pro версии, вы будете иметь доступ к этим визуальным функциям и особенно поможете реализации общественных проектов. + Эти визуальные функции доступны благодаря вашей щедрости. - Для того, чтобы сохранить нашу свободу и быть всегда активными, мы рассчитываем на ваш вклад. - + Для того, чтобы сохранить нашу свободу и быть всегда активными, мы рассчитываем на ваш вклад. + Эта функция на в разработке и требует вашего участия, чтобы быть доступной в ближайшее время. Покупая Pro версию, - Участвуя в проекте, + + Участвуя в проекте, вы поощряете разработчиков создавать новые возможности и исправлять ошибки в соответствии с вашими замечаниями. Спасибо большое за ваш вклад. Мы прилагаем все усилия, чтобы быстро выпустить эту функцию. Не забывайте обновлять приложение. - Скачать Помощь проекту - ChaCha20 - Функция формирования ключа AES Argon2 - Выбрать тему Изменить тему приложения, изменив цвета "Выбрать набор значков " Изменить набор значков приложения - - + Magikeyboard + Magikeyboard (KeePass DX) + Настройки Magikeyboard + \ No newline at end of file From 2208c67b6272551d255d0aa3ac70fa006639bcaf Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 28 Feb 2019 10:46:35 +0100 Subject: [PATCH 070/289] Fix renaming groups #194 --- .../java/com/kunzisoft/keepass/activities/GroupActivity.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index eafa8fc49..9fdd42063 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -998,13 +998,14 @@ public class GroupActivity extends LockingActivity // If update add new elements if (oldGroupToUpdate != null) { PwGroup updateGroup = oldGroupToUpdate.clone(); - updateGroup.setName(name); try { iconStandard = (PwIconStandard) icon; updateGroup = ((PwGroupV4) oldGroupToUpdate).clone(); // TODO generalize } catch (Exception e) { e.printStackTrace(); - } // TODO custom icon + } + updateGroup.setName(name); + // TODO custom icon updateGroup.setIconStandard(iconStandard); if (listNodesFragment != null) From 620b1b4f021db978b941b41e29d2c9fe057cfa62 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 28 Feb 2019 16:34:25 +0100 Subject: [PATCH 071/289] Refactor database runnable and prevent activity to be closed during saving --- .../keepass/activities/GroupActivity.java | 2 +- .../activities/lock/LockingActivity.kt | 17 +++---- .../ActionWithSaveDatabaseRunnable.java | 47 ------------------- .../AssignPasswordInDatabaseRunnable.java | 5 +- .../database/action/SaveDatabaseRunnable.java | 31 ++++++++++-- .../node/ActionNodeDatabaseRunnable.java | 4 +- .../keepass/settings/SettingsActivity.kt | 2 +- ...aseSavePreferenceDialogFragmentCompat.java | 2 +- .../keepass/timeout/TimeoutHelper.kt | 31 +++++++++++- 9 files changed, 73 insertions(+), 68 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/ActionWithSaveDatabaseRunnable.java diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 9fdd42063..995aff137 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -327,7 +327,7 @@ public class GroupActivity extends LockingActivity private void openGroup(PwGroup group, boolean isASearch) { // Check TimeoutHelper - TimeoutHelper.INSTANCE.lockOrResetTimeout(this, () -> { + TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeoutOrResetTimeout(this, () -> { // Open a group in a new fragment ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, getReadOnly(), isASearch); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index c91d43a22..9e2e08212 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -143,13 +143,14 @@ abstract class LockingActivity : StylishActivity() { inner class LockReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - val action = intent.action - if (action != null) { - when (action) { - Intent.ACTION_SCREEN_OFF -> if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this@LockingActivity)) { - lockAndExit() + if (!TimeoutHelper.temporarilyDisableTimeout) { + intent.action?.let { + when (it) { + Intent.ACTION_SCREEN_OFF -> if (PreferencesUtil.isLockDatabaseWhenScreenShutOffEnable(this@LockingActivity)) { + lockAndExit() + } + LOCK_ACTION -> lockAndExit() } - LOCK_ACTION -> lockAndExit() } } } @@ -166,7 +167,7 @@ abstract class LockingActivity : StylishActivity() { views.forEach { it.setOnFocusChangeListener { _, hasFocus -> if (hasFocus) { - TimeoutHelper.lockOrResetTimeout(this) + TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) } } } @@ -174,7 +175,7 @@ abstract class LockingActivity : StylishActivity() { override fun onBackPressed() { if (timeoutEnable) { - TimeoutHelper.lockOrResetTimeout(this) { + TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) { super.onBackPressed() } } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/ActionWithSaveDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/ActionWithSaveDatabaseRunnable.java deleted file mode 100644 index e6a7075eb..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/ActionWithSaveDatabaseRunnable.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.kunzisoft.keepass.database.action; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; - -public abstract class ActionWithSaveDatabaseRunnable extends RunnableOnFinish { - - protected Context mContext; - protected boolean mDontSave; - protected Database mDatabase; - - public ActionWithSaveDatabaseRunnable(Context context, Database database, OnFinishRunnable finish, boolean dontSave) { - super(finish); - - this.mDatabase = database; - this.mContext = context; - this.mDontSave = dontSave; - this.mFinish = new AfterActionRunnable(finish); - } - - @Override - public void run() { - // Commit to disk - SaveDatabaseRunnable save = new SaveDatabaseRunnable(mContext, mDatabase, mFinish, mDontSave); - save.run(); - } - - public void runWithoutSaveDatabase() { - mFinish.run(); - } - - abstract protected void onFinish(boolean success, String message); - - private class AfterActionRunnable extends OnFinishRunnable { - - AfterActionRunnable(OnFinishRunnable finish) { - super(finish); - } - - @Override - public void run() { - onFinish(mSuccess, mMessage); - super.run(); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java index 0ec5f3446..7bddca7d3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java @@ -30,7 +30,7 @@ import com.kunzisoft.keepass.utils.UriUtil; import java.io.IOException; import java.io.InputStream; -public class AssignPasswordInDatabaseRunnable extends ActionWithSaveDatabaseRunnable { +public class AssignPasswordInDatabaseRunnable extends SaveDatabaseRunnable { private String mPassword; private Uri mKeyfile; @@ -63,7 +63,8 @@ public class AssignPasswordInDatabaseRunnable extends ActionWithSaveDatabaseRunn } catch (InvalidKeyFileException|IOException e) { erase(mBackupKey); finish(false, e.getMessage()); - super.runWithoutSaveDatabase(); + // run without save database + mFinish.run(); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java index 49464b3c7..960ca0a00 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java @@ -23,13 +23,14 @@ import android.content.Context; import com.kunzisoft.keepass.database.Database; import com.kunzisoft.keepass.database.exception.PwDbOutputException; +import com.kunzisoft.keepass.timeout.TimeoutHelper; import java.io.IOException; public class SaveDatabaseRunnable extends RunnableOnFinish { - private Context mContext; - private Database mDatabase; + protected Context mContext; + protected Database mDatabase; private boolean mDontSave; public SaveDatabaseRunnable(Context context, Database database, OnFinishRunnable finish, boolean dontSave) { @@ -38,6 +39,7 @@ public class SaveDatabaseRunnable extends RunnableOnFinish { this.mContext = context; this.mDatabase = database; this.mDontSave = dontSave; + this.mFinish = new AfterActionRunnable(finish); } public SaveDatabaseRunnable(Context ctx, Database db, OnFinishRunnable finish) { @@ -46,8 +48,8 @@ public class SaveDatabaseRunnable extends RunnableOnFinish { @Override public void run() { - - if ( ! mDontSave ) { + TimeoutHelper.INSTANCE.temporarilyDisableTimeout(); + if (!mDontSave) { try { mDatabase.saveData(mContext); } catch (IOException e) { @@ -59,8 +61,27 @@ public class SaveDatabaseRunnable extends RunnableOnFinish { return; } } - finish(true); } + /** + * Method called when the database finish to save data + * @param success 'true' if save is ok, 'false' elsewhere + * @param message + */ + protected void onFinish(boolean success, String message) {} + + private class AfterActionRunnable extends OnFinishRunnable { + + AfterActionRunnable(OnFinishRunnable finish) { + super(finish); + } + + @Override + public void run() { + TimeoutHelper.INSTANCE.releaseTemporarilyDisableTimeoutAndLockIfTimeout(mContext); + onFinish(mSuccess, mMessage); + super.run(); + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java index b43b00605..4a5a0870b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java @@ -4,9 +4,9 @@ import android.content.Context; import com.kunzisoft.keepass.database.Database; import com.kunzisoft.keepass.database.PwNode; -import com.kunzisoft.keepass.database.action.ActionWithSaveDatabaseRunnable; +import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable; -abstract class ActionNodeDatabaseRunnable extends ActionWithSaveDatabaseRunnable { +abstract class ActionNodeDatabaseRunnable extends SaveDatabaseRunnable { private AfterActionNodeOnFinish callbackRunnable; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 9af21ceed..8b12ed677 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -117,7 +117,7 @@ open class SettingsActivity : LockingActivity(), MainPreferenceFragment.Callback override fun onNestedPreferenceSelected(key: NestedSettingsFragment.Screen) { if (timeoutEnable) - TimeoutHelper.lockOrResetTimeout(this) { + TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) { replaceFragment(key) } else diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java index 58fe42c11..7f0e282be 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java @@ -47,7 +47,7 @@ public abstract class DatabaseSavePreferenceDialogFragmentCompat extends InputP assert getActivity() != null; if (database != null && afterSaveDatabase != null) { - SaveDatabaseRunnable saveDatabaseRunnable = new SaveDatabaseRunnable(getContext(), + SaveDatabaseRunnable saveDatabaseRunnable = new SaveDatabaseRunnable(getActivity(), database, afterSaveDatabase); saveDatabaseRunnable.setUpdateProgressTaskStatus( diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index d94397a5c..9227aea1b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -40,6 +40,9 @@ object TimeoutHelper { private const val TAG = "TimeoutHelper" + var temporarilyDisableTimeout = false + private set + private fun getLockPendingIntent(context: Context): PendingIntent { return PendingIntent.getBroadcast(context, REQUEST_ID, @@ -79,9 +82,14 @@ object TimeoutHelper { /** * Check the time previously record with recordTime and do the [timeoutAction] if timeout + * if temporarilyDisableTimeout() is called, the function as no effect until releaseTemporarilyDisableTimeoutAndCheckTime() is called * return 'false' if timeout, 'true' if in time */ fun checkTime(context: Context, timeoutAction: (() -> Unit)? = null): Boolean { + // No effect if temporarily disable + if (temporarilyDisableTimeout) + return true + // Cancel the lock PendingIntent if (App.getDB().loaded) { val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager @@ -133,10 +141,31 @@ object TimeoutHelper { } } - fun lockOrResetTimeout(activity: Activity, action: (() -> Unit)? = null) { + /** + * Check the time previously record then, if timeout lock the activity, else reset the timer + */ + fun checkTimeAndLockIfTimeoutOrResetTimeout(activity: Activity, action: (() -> Unit)? = null) { if (checkTimeAndLockIfTimeout(activity)) { recordTime(activity) action?.invoke() } } + + /** + * Temporarily disable timeout, checkTime() function always return true + */ + fun temporarilyDisableTimeout() { + temporarilyDisableTimeout = true + } + + /** + * Release the temporarily disable timeout and directly call checkTime() + */ + fun releaseTemporarilyDisableTimeoutAndLockIfTimeout(context: Context): Boolean { + temporarilyDisableTimeout = false + return if (context is LockingActivity) + checkTimeAndLockIfTimeout(context) + else + checkTime(context) + } } \ No newline at end of file From 56912ddaf70229f974ee3e128a567816fe1ebf66 Mon Sep 17 00:00:00 2001 From: George Date: Sun, 3 Mar 2019 06:55:54 +0000 Subject: [PATCH 072/289] Translated using Weblate (Chinese (Simplified)) Currently translated at 71.3% (246 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 105 +++++++++++++++------ 1 file changed, 77 insertions(+), 28 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index bc26cef5c..ae2961c7b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1,4 +1,4 @@ - + - +--> 反馈 主页 KeePass 密码管理器在 Android 平台上的实现 @@ -134,28 +133,26 @@ 大写 请授予 SD 卡写入权限来保存数据库。 您的 SD 卡目前尚未挂载至您的设备上。无法加载或创建您的数据库。 - 输入密码和/或一个密钥文件来解锁你的数据库。 \n \n记得在每次更改后将您的 .kdbx 文件备份至安全的地方。 - - 5秒 - 10秒 - 20秒 - 30秒 - 1分钟 - 5分钟 - 15分钟 - 30分钟 - 从不 + 5秒 + 10秒 + 20秒 + 30秒 + 1分钟 + 5分钟 + 15分钟 + 30分钟 + 从不 - - - + + + -添加字符串 + 添加字符串 加密 不再显示 ASCⅡ 拓展区字符 @@ -165,15 +162,12 @@ 无法清空剪切板 滑动以清空剪切板 ChaCha20 - AES KDF Argon2 - 选择主题 选择图标包 切换本应用中使用的图标风格 - -编辑条目 + 编辑条目 密钥推导函数 找不到条目。 无法加载数据库。 @@ -224,7 +218,7 @@ 外观 通用 自动填充 - KeePass DX 自动填充服务 + KeePass DX 表单自动填充 使用 KeePass DX 登录 剪贴板 剪贴板通知 @@ -242,23 +236,78 @@ 文字样式 应用外观 其他 - 键盘 魔法键盘 魔法键盘设置 默认只读 默认以只读方式打开数据库 - 下载 贡献 - 选择各种颜色的应用主题 每个字符串必须有一个字段名。 字段名 字段值 - %1 的副本 + 副本 %1$s 最低优先 ↓ 使用 Android 存储访问框架 (SAF)浏览文件(需要 KitKat 或更高) 存储访问框架 请避免在 .kbd 文件中保存含有 Latin-1 以外的其他字符的密码,因为这些字符将会被转换为同一字符。 - + 历史文件夹 + 底部回收站 + 你真的不需要密码解锁的保护吗? + 你确认不使用任何的密钥吗? + 生成 %1$s + 指纹解锁功能支持,但没有配置使用。 + 指纹扫描 + 加密密码存储 + 不能读取指纹密钥,请重置你的密码。 + 未能识别的指纹 + 指纹识别错误:%1$s + 当前数据库没有任何密码。 + 配置自动填充服务 + 打开自动填充功能,以便快速的在其他应用中填写信息 + 密码生成长度 + 设置密码生成的初始长度 + 密码字符集 + 设置密码生成时所使用的字符集 + 打开剪切板通知功能以复制输入字段 + 如果剪切板自动删除记录失败,则手动删除历史记录。 + 屏幕锁定 + 当屏幕熄灭时锁定数据库 + 如何设置指纹扫描以快速解锁? + 从你的设备保存已经扫描的指纹 + \"设置\" → \"安全\" → \"指纹\" + 输入数据库密码 + 扫描你的指纹,以便安全的存储数据库密码。 + 当数据库密码功能关闭的时候,使用指纹扫描解锁数据库。 + 使用 + 指纹扫描 + 通过指纹扫描解锁数据库 + 删除数据库加密所使用的密钥 + 删除所有与指纹解锁相关的加密密钥 + 确认要删除所有与指纹识别相关的密钥吗? + 无法启动此功能。 + 你的安卓版本 %1$s 不能满足软件对最低系统版本 %2$s 的要求。 + 找不到所需的硬件。 + 分配主键 + 启用回收站 + 在正式删除文件夹和项目之前先移动到回收站 + KeePass DX 需要外部存储设备存储权限以便写入数据库。 + KeePass DX 需要外部设备存储权限以便读取一个不通过内容提供者提供的URI(统一资源标识符)。 + 未能获取外部存储设备权限。 + 没有外部存储设备权限的情况下,不能执行此操作。 + 字段显示优化 + 在字段中改变使用的字体以便优化内容的展示 + 选择需要打开的文件 + 自动打开在文件浏览器中选中的文件 + 允许剪切板保存输入的密码以及受保护的字段 + 警告:所有应用都可以访问剪切板,如果剪切板存在敏感数据,可能会被其他软件存储在剪切板的数据覆盖。 + 警告:关闭此功能可能会导致打开或者保存数据库失败。 + 打开数据库文件链接 + 激活自定义键盘,填充密码和所有标识字段 + 设置安全键盘填充表单。 + 在设备设置中激活“Magikeyboard”。 + “设置”→“语言和输入”→“当前键盘”,并选择所需要用于输入的键盘。 + 或(“设置”→“语言和输入”→“虚拟键盘”,并选择所需要用于输入的键盘) + 当填写表单时,请选择“Magikeyboard”键盘。 + \ No newline at end of file From f469fa224e663f51c95138b8c24ff73095b796d7 Mon Sep 17 00:00:00 2001 From: Nils B Date: Sat, 2 Mar 2019 12:28:42 +0000 Subject: [PATCH 073/289] Translated using Weblate (German) Currently translated at 99.7% (344 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 60 +++++++++----------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f2ecb9979..53066e0df 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1,4 +1,4 @@ - + - +--> Rückmeldung Webseite Android-Implementierung des Passwortmanagers KeePass @@ -166,28 +165,25 @@ Gewähren Sie Schreibzugriff auf die SD-Karte, um Datenbankänderungen speichern zu können. Hängen Sie die SD-Karte ein, um eine Datenbank erstellen oder laden zu können. Version %1$s - Geben Sie ein Passwort und/oder eine Schlüsseldatei zum Entsperren Ihrer Datenbank ein. \n \nDenken Sie daran, nach jeder Änderung eine Kopie Ihrer .kdbx-Datei an einem sicheren Ort abzuspeichern. - - 5 Sekunden - 10 Sekunden - 20 Sekunden - 30 Sekunden - 1 Minute - 5 Minuten - 15 Minuten - 30 Minuten - Nie + 5 Sekunden + 10 Sekunden + 20 Sekunden + 30 Sekunden + 1 Minute + 5 Minuten + 15 Minuten + 30 Minuten + Nie - Klein - Mittel - Groß + Klein + Mittel + Groß - Sind Sie sicher, dass das Entsperren ohne Passwort möglich sein soll\? Sind Sie sicher, dass Sie keinen Verschlüsselungsschlüssel verwenden wollen ? Aussehen @@ -228,7 +224,7 @@ App-Design, welches in der App genutzt wird Verschlüsselung Schlüsselableitungsfunktion - Erweitertes ASCII + Erweiterter ASCII Erlauben Wischen, um Zwischenablage jetzt zu leeren Autofill-Dienst kann nicht aktiviert werden. @@ -288,7 +284,6 @@ Tastatur Magikeyboard Eine eigene Tastatur zum einfachen Ausfüllen aller Passwort- und Identitätsfelder aktivieren - Hilfe-Anzeige wiederholen Alle Hilfe-Themen noch einmal anzeigen Hilfe-Anzeige zurückgesetzt @@ -323,12 +318,12 @@ Auswählen, wie Einträge und Gruppen sortiert werden. Mitmachen Machen Sie mit, damit sich Stabilität, Sicherheit verbessern und weitere Funktionen hinzukommen. - Anders als viele andere Passwortmanager ist dieser werbefrei, quelloffen und speichert keine persönlichen Daten auf dessen Servern - unabhängig von der Version, die Sie nutzen. - Mit dem Kauf der Pro-Version erhalten Sie Zugang zu dieser visuellen Funktion und Sie unterstützen insbesondere die Umsetzung gemeinschaftlicher Projektarbeiten. + Mit dem Kauf der Pro-Version erhalten Sie Zugang zu dieser visuellen Funktion und Sie unterstützen insbesondere die Umsetzung gemeinschaftlicher Projektarbeiten. + Diese visuelle Funktion wurde wegen Ihrer Großzügigkeit freigeschaltet. - Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren Beitrag. - + Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren Beitrag. + Diese Funktion ist in Entwicklung und erfordert Ihren Beitrag, um bald verfügbar zu sein. Durch den Kauf der Pro-Version, Durch Ihr Mitwirken, @@ -336,18 +331,14 @@ Vielen Dank für Ihre Unterstützung. Wir bemühen uns, diese Funktion bald zu veröffentlichen. Vergessen Sie nicht, Ihre App aktuell zu halten indem sie neue Versionen installieren. - Download Unterstützen - ChaCha20 - AES KDF Argon2 - Icon-Paket In der App verwendetes Icon-Packet -Eine Gruppe kann nicht in sich selbst verschoben werden. + Eine Gruppe kann nicht in sich selbst verschoben werden. Kopieren Verschieben Einfügen @@ -365,10 +356,8 @@ Füllen Sie die Felder mit den Elementen des Eintrags aus. Sperren Sie die Datenbank. Kehren Sie zur Standardtastatur zurück. - Kein Passwort zulassen \"Öffnen\"-Taste aktivieren, wenn keine Passwort-Identifikation festgelegt ist - Hilfe-Anzeige Bedienelemente hervorheben, um die Funktionsweise der App zu lernen veränderbar @@ -376,7 +365,6 @@ Schreibgeschützt Schreibschutz aktivieren Datenbank standardmäßig schreibgeschützt öffnen - Ändern Sie den Öffnungsmodus für die Sitzung. \n \n\"Write-protected\" verhindert unbeabsichtigte Änderungen an der Datenbank. @@ -390,25 +378,19 @@ Magikeyboard Magikeyboard (KeePass DX) Magikeyboard-Einstellungen - Eingabe - Zeitsperre Zeit nach der Tastatureingaben gelöscht werden - Benachrichtigung Benachrichtigung anzeigen, wenn eine Eingabe abrufbar ist Eingabe %1$s über Magikeyboard abrufbar %1$s - Beim Schließen löschen Die Tastatureingabe löschen, wenn die Benachrichtung geschlossen wird Aussehen - Tastaturdesign - Tasten Vibration auf Tastendruck Ton auf Tastendruck - + \ No newline at end of file From 1cf8b1fc7de11535f8efe58114ca2d9c64db14e9 Mon Sep 17 00:00:00 2001 From: WaldiS Date: Sat, 2 Mar 2019 20:06:04 +0000 Subject: [PATCH 074/289] Translated using Weblate (Polish) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/ --- app/src/main/res/values-pl/strings.xml | 61 ++++++++++---------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index cd787d383..de35ce55d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1,4 +1,4 @@ - + - +--> Informacje zwrotne Strona domowa Implementacja Android dla menedżera haseł KeePass @@ -139,28 +138,26 @@ along with KeePass DX. If not, see . Dostęp do pamięci masowej Przydziel dostęp do zapisu na karcie SD, aby zapisać zmiany w bazie danych. Zamontuj kartę SD, aby utworzyć lub załadować bazę danych. - prowadź hasło i/lub plik klucza, aby odblokować bazę danych. \n \nUtwórz kopię zapasową pliku bazy danych w bezpiecznym miejscu po każdej zmianie. - - 5 sekund - 10 sekund - 20 sekund - 30 sekund - 1 minuta - 5 minut - 15 minut - 30 minut - Nigdy + 5 sekund + 10 sekund + 20 sekund + 30 sekund + 1 minuta + 5 minut + 15 minut + 30 minut + Nigdy - Mała - Średnia - Duża + Mała + Średnia + Duża -Edytuj wpis + Edytuj wpis Dodaj ciąg Szyfrowanie Funkcja generująca klucz @@ -186,7 +183,7 @@ along with KeePass DX. If not, see . Plik klucza jest pusty. Pokaż nazwy użytkowników Pokaż nazwy użytkowników na listach wpisów - Kopiuj z %1$s + Skopiowano %1$s Wypełnianie formularzy Kopiuj Przenieś @@ -291,7 +288,6 @@ along with KeePass DX. If not, see . Tekst Aplikacja Inne - Klawiatura Magikeyboard Aktywuj niestandardową klawiaturę wypełniającą hasła i wszystkie pola tożsamości @@ -306,12 +302,10 @@ along with KeePass DX. If not, see . Wypełnij swoje pola, używając elementów pozycji. Zablokuj bazę danych. Użyj ponownie domyślnej klawiatury. - Nie zezwalaj na żadne hasło Włącz przycisk \"Otwórz\", jeśli nie wybrano identyfikacji hasła Ochrona przed zapisem Domyślnie otwarte bazy danych są tylko do odczytu - Ekrany edukacyjne Zaznacz elementy, aby dowiedzieć się, jak działa aplikacja Zresetuj ekrany edukacyjne @@ -354,12 +348,12 @@ along with KeePass DX. If not, see . Wybierz sposób sortowania wpisów i grup. Weź udział Pomóż zwiększyć stabilność, bezpieczeństwo i dodawanie kolejnych funkcji. - W przeciwieństwie do wielu aplikacji do zarządzania hasłami, ta jest wolna od reklam, jest oprogramowaniem darmowym typu copylefted libre i nie zbiera danych osobowych na swoich serwerach, bez względu na to, jakiej wersji używasz. - Kupując wersję pro, będziesz mieć dostęp do tej funkcja wizualna a szczególnie pomożesz zrealizować projekty społecznościowe. + Kupując wersję pro, będziesz mieć dostęp do tej funkcja wizualna a szczególnie pomożesz zrealizować projekty społecznościowe. + Ta funkcja wizualna jest dostępna dzięki Twojej hojności. - Aby zachować naszą wolność i być zawsze aktywnym, liczymy na Twój wkład. - + Aby zachować naszą wolność i być zawsze aktywnym, liczymy na Twój wkład. + Ta funkcja jest rozwojowa i wymaga twojego wkładu aby być wkrótce dostępną. Kupując wersję pro wersja, Przez przyczynianie się, @@ -367,43 +361,32 @@ along with KeePass DX. If not, see . Wielkie dzięki za twój wkład. Ciężko pracujemy, aby szybko udostępnić tę funkcję. Nie zapomnij o aktualizacji aplikacji, instalując nowe wersje. - Pobieranie Przyczyń się - ChaCha20 - AES KDF Argon2 - Motyw aplikacji Motyw używany w aplikacji Pakiet ikon Pakiet ikon używany w aplikacji - -Kompilacja %1$s + Kompilacja %1$s Magikeyboard Magikeyboard (KeePass DX) Ustawienia Magikeyboard - Wpis - Limit czasu Limit czasu, aby wyczyścić wpis klawiatury - Informacje dotyczące powiadomień Pokaż powiadomienie, gdy wpis jest dostępny Wpis %1$s dostępne na Magikeyboard %1$s - Wyczyść przy zamykaniu Wyczyść wpis klawiatury podczas zamykania powiadomienia Wygląd - Motyw klawiatury - Klawiatura Wibracja po naciśnięciu klawisza Dźwięk przy naciśnięciu - + \ No newline at end of file From 8de86e26d6f9a9577ac58615c5452901983cbfbc Mon Sep 17 00:00:00 2001 From: George Date: Wed, 6 Mar 2019 14:28:02 +0000 Subject: [PATCH 075/289] Translated using Weblate (Chinese (Simplified)) Currently translated at 83.8% (289 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 59 +++++++++++++++++----- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ae2961c7b..4898fae12 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -132,7 +132,7 @@ 不支持的数据库版本。 大写 请授予 SD 卡写入权限来保存数据库。 - 您的 SD 卡目前尚未挂载至您的设备上。无法加载或创建您的数据库。 + 请挂载SD卡以创建或加载数据库。 输入密码和/或一个密钥文件来解锁你的数据库。 \n \n记得在每次更改后将您的 .kdbx 文件备份至安全的地方。 @@ -227,7 +227,7 @@ 文件名 路径 创建新数据库 - B + 字节数 显示文件路径 显示完整的文件路径 数据库名称 @@ -237,9 +237,9 @@ 应用外观 其他 键盘 - 魔法键盘 + Magikeyboard键盘 魔法键盘设置 - 默认只读 + 写入保护(只读模式) 默认以只读方式打开数据库 下载 贡献 @@ -275,15 +275,15 @@ 屏幕锁定 当屏幕熄灭时锁定数据库 如何设置指纹扫描以快速解锁? - 从你的设备保存已经扫描的指纹 + 通过您设备的安全设置保存您的指纹: \"设置\" → \"安全\" → \"指纹\" 输入数据库密码 扫描你的指纹,以便安全的存储数据库密码。 当数据库密码功能关闭的时候,使用指纹扫描解锁数据库。 - 使用 + 使用指纹解锁 指纹扫描 通过指纹扫描解锁数据库 - 删除数据库加密所使用的密钥 + 删除加密密钥 删除所有与指纹解锁相关的加密密钥 确认要删除所有与指纹识别相关的密钥吗? 无法启动此功能。 @@ -291,13 +291,13 @@ 找不到所需的硬件。 分配主键 启用回收站 - 在正式删除文件夹和项目之前先移动到回收站 + 在彻底删除文件夹和项目之前先移动到回收站 KeePass DX 需要外部存储设备存储权限以便写入数据库。 - KeePass DX 需要外部设备存储权限以便读取一个不通过内容提供者提供的URI(统一资源标识符)。 + KeePass DX 需要外部设备存储权限以读取一个不通过内容提供者提供的统一资源标识符。 未能获取外部存储设备权限。 没有外部存储设备权限的情况下,不能执行此操作。 字段显示优化 - 在字段中改变使用的字体以便优化内容的展示 + 在字段中改变使用的字体以优化内容的展示 选择需要打开的文件 自动打开在文件浏览器中选中的文件 允许剪切板保存输入的密码以及受保护的字段 @@ -305,9 +305,44 @@ 警告:关闭此功能可能会导致打开或者保存数据库失败。 打开数据库文件链接 激活自定义键盘,填充密码和所有标识字段 - 设置安全键盘填充表单。 - 在设备设置中激活“Magikeyboard”。 + 选择“Magikeyboard键盘”以安全地自动填充表单。 + 在设备设置中激活“Magikeyboard键盘”。 “设置”→“语言和输入”→“当前键盘”,并选择所需要用于输入的键盘。 或(“设置”→“语言和输入”→“虚拟键盘”,并选择所需要用于输入的键盘) 当填写表单时,请选择“Magikeyboard”键盘。 + 剪切板权限 + 通过长时间按住“空格键”以切换键盘,如果“空格键”不可用,则: + 自定义用以切换键盘的按键。 + 使用输入元素填充字段。 + 锁定数据库。 + 再次使用默认键盘。 + Magikeyboard键盘 + Magikeyboard键盘(KeePass DX) + Magikeyboard键盘设置 + 输入 + 超时设置 + 超时则清空键盘输入 + 通知信息 + 当输入可用的时候显示通知信息 + 输入 + %1$s 在Magikeyboard键盘中可用 + %1$s + 当关闭时清空输入 + 当通知关闭的时候清空键盘输入 + 偏好 + 键盘主题 + 按键 + 按键震动 + 按键声音 + 允许空密码 + 如果没有选择密码验证,则启用“打开”按钮。 + 新手引导模式 + 高亮按钮以学习如何使用KeePass Dx + 重置新手引导模式 + 再次显示所有入门引导项目 + 重置新手引导模式 + 创建您的数据库文件 + 创建您的第一个密码管理文件。 + 打开一个已有数据库 + 从您的文件浏览器中打开并使用一个历史数据库。 \ No newline at end of file From 821dc6df63353ee537070b06b7f71f74eae4ca8d Mon Sep 17 00:00:00 2001 From: Ldm Public Date: Wed, 6 Mar 2019 06:59:43 +0000 Subject: [PATCH 076/289] Translated using Weblate (French) Currently translated at 99.7% (344 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 51 +++++++++----------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2c691cff4..ac9739ca0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -115,7 +115,7 @@ Masquer les mots de passe (***) par défaut À propos Modifier la clé maîtresse - Copie de %1$s + %1$s copié Paramètres Paramètres de la base de données Supprimer @@ -259,7 +259,6 @@ Clavier Magikeyboard Activer un clavier personnalisé pour remplir vos mots de passe et tous les champs d’identité - Écrans éducatifs Met en surbrillance les éléments pour apprendre le fonctionnement de l’application Réinitialiser les écrans éducatifs @@ -299,12 +298,11 @@ Choisir comment les entrées et les groupes sont triés. Participer Aider à améliorer la stabilité, la sécurité et à ajouter des fonctionnalités. - Contrairement à beaucoup d’applications de gestion de mots de passe, cette application est sans publicité, libre sous licence copyleft et ne collecte pas de données personnelles sur ses serveurs, peu importe la version que vous utilisez. En achetant la version pro, vous accéderez à cette <strong>fonctionnalité visuelle</strong> et vous aiderez en particulier à <strong>la réalisation de projets communautaires.</strong> Cette <strong>fonctionnalité visuelle</strong> est disponible grâce à votre générosité. - Afin de garder notre liberté et de toujours être actifs, nous comptons sur votre contribution. - + Afin de garder notre liberté et de toujours être actifs, nous comptons sur votre contribution. + Cette fonctionnalité est en cours de développement et nécessite votre contribution pour être bientôt disponible. En achetant la version <strong>pro</strong>, En <strong>contribuant</strong>, @@ -312,36 +310,31 @@ Merci beaucoup pour votre contribution. Nous travaillons dur pour livrer cette fonctionnalité rapidement. N’oubliez pas de garder votre application à jour en installant les nouvelles versions. - Télécharger Contribuer - Rijndael (AES) Twofish ChaCha20 - Fonction de dérivation de clé AES Argon2 - - 5 secondes - 10 secondes - 20 secondes - 30 secondes - 1 minute - 5 minutes - 15 minutes - 30 minutes - Jamais + 5 secondes + 10 secondes + 20 secondes + 30 secondes + 1 minute + 5 minutes + 15 minutes + 30 minutes + Jamais - Petit - Moyen - Grand + Petit + Moyen + Grand - Thème de l’application Thème utilisé dans l’application @@ -354,8 +347,7 @@ Ensemble d’icônes Ensemble d’icônes utilisés dans l’application - -Vous ne pouvez pas déplacer un groupe dans lui-même. + Vous ne pouvez pas déplacer un groupe dans lui-même. Copier Déplacer Coller @@ -371,15 +363,12 @@ Remplisser vos champs avec les éléments de l’entrée. Verrouiller la base de données. Utiliser à nouveau le clavier par défaut. - N’autoriser aucun mot de passe Activer le bouton « Ouvrir » si aucune identification par mot de passe n’est sélectionnée - Protégé en écriture Modifiable Protégé en écriture Ouvrir votre base de données en lecture seule par défaut - Protéger en écriture votre base de données Changer le mode d’ouverture pour la session. \n @@ -395,25 +384,19 @@ Magikeyboard Magikeyboard (KeePass DX) Paramètres Magikeyboard - Entrée - Délai d’expiration Délai d’expiration pour effacer l’entrée au clavier - Informations de notification Afficher une notification lorsqu’une entrée est disponible Entrée 1$s disponible sur Magikeyboard %1$s - Effacer à la fermeture Effacer l’entrée au clavier lors de la fermeture de la notification Apparence - Thème du clavier - Touches Vibrer au toucher Son au toucher - \ No newline at end of file + \ No newline at end of file From 6db55b5eaac6e35d02147d4bf760c217e92aa1b9 Mon Sep 17 00:00:00 2001 From: abidin toumi Date: Thu, 7 Mar 2019 16:51:21 +0000 Subject: [PATCH 077/289] Translated using Weblate (Arabic) Currently translated at 49.9% (172 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/ --- app/src/main/res/values-ar/strings.xml | 65 +++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index ac847762f..df637497e 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,4 +1,4 @@ - + - +--> الصفحة الرئيسية : قبول إضافة مجموعة @@ -89,7 +88,7 @@ ناقص أبداً لا توجد نتائج للبحث - لا يوجد عنوان لهذا الرابط. + ثبت متصفح لزيارة هذا الرابط. إنشاء قاعدة بيانات جديدة … الحماية للقراءة فقط @@ -122,8 +121,7 @@ نسخ حقل تأمين قاعدة البيانات الملاحظات : - -تنفيذ أندرويد لمدير كلمات السر «كي‌باس» + تنفيذ أندرويد لمدير كلمات السر «كي‌باس» إضافة مدخلة تحرير مدخلة وظيفة اشتقاق المفتاح @@ -163,4 +161,57 @@ ملف المفتاح فارغ. أظهر أسماء المستخدمين أظهر أسماء المستخدمين في قوائم المدخلات - + كلمةالسر المشفرة + ملف قفل + اخفاء كلمات السر + نُسخ %1$s + نسخ + نقل + لصق + الغاء + اخفاء كلمة السر + اظهار كلمة السر + الانتقال الى الرابط + محمي من التعديل + قابل للتعديل + فتح قاعدة بيانات موجودة + انشاء قاعدة بيانات + قواعد البيانات الاخيرة + لا تبحثفي مدخلات النسخ الاحتياطي + قيد العمل… + KeePass DX يحتاج صلاحية الكتابة من اجل تعديل قاعدة البيانات. + ابتداءا من اندرويد كيت كات بعض الاجهزة لا تسمح بالكتابة على قاعدة البيانات. + تذكر موقع ملف قفل قاعدة البيانات + حفظ ملف القفل + خوارزمية تشفير جميع البيانات. + قاعدة بيانات غير مدعومة. + اسمح بالكتابة على بطاقة الذاكرة لحفظ التغيرات. + اربط بطاقة الذاكرة لإنشاء او تحميل قاعدة بيانات. + بناء %1$s + تم حفظ كلمة السر المشفرة + قاعدة البيانات لا تمتلك كلمة سر. + مظهر + عام + ملأ تلقائي + سجل باستخدام KeePass DX + تعيين خدمة الملأ التلقائي الافتراضية + حجم كلمة السر المولدة + تعيين الحجم الافتراضي لكلمات السر المولدة + محارف كلمة السر + تعيين المحارف المسموح بها لتوليد كلمة السر + حافظة + اشعارات الحافظة + اذا فشل الحذف التلقائي من الحافظة ,احذف تأريخه يدويا + قفل الشاشة + اقفل قاعدة البيانات عند انغلاق الشاشة + ادخل كلمة سر قاعدة البيانات + استخدام + حذف مفاتيح التشفير + لا يمكن بدأ هذه الميزة . + نسخة الاندرويد %1$s لا تحقق ادنى متطلبات السنخة %2$s. + اسم الملف + مسار + بايت + مسار الملف + عرض المسار الكامل للملف + \ No newline at end of file From 54883bb0439a4938866907228a2e190bb2dab0cf Mon Sep 17 00:00:00 2001 From: George Date: Sat, 9 Mar 2019 13:30:56 +0000 Subject: [PATCH 078/289] Translated using Weblate (Chinese (Simplified)) Currently translated at 84.6% (292 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4898fae12..7fa5d3b3a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -345,4 +345,16 @@ 创建您的第一个密码管理文件。 打开一个已有数据库 从您的文件浏览器中打开并使用一个历史数据库。 + 一个指向您文件的链接可用 + 您可以通过物理链接打开您的数据库(例如:file://或者content://)。 + 为您的数据库新增项目 + 用户记录会帮助管理您的密码。 +\n分组文件夹将组织数据库中的用户记录。 + 搜索用户记录 + 输入标题、用户名或其他字段的内容来检索密码。 + 数据库解锁指纹 + 将您的密码链接到您扫描的指纹,以快速解锁您的数据库。 + 编辑记录 + 使用自定义字段编辑记录。数据可以在不同的输入字段之间引用。 + 为您的记录创建一个复杂的密码。 \ No newline at end of file From 4bdf99b2f442d8529c5684de89b6e01f29182acd Mon Sep 17 00:00:00 2001 From: abidin toumi Date: Sun, 10 Mar 2019 15:56:34 +0000 Subject: [PATCH 079/289] Translated using Weblate (Arabic) Currently translated at 66.1% (228 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/ --- app/src/main/res/values-ar/strings.xml | 58 ++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index df637497e..370677160 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -214,4 +214,62 @@ بايت مسار الملف عرض المسار الكامل للملف + فحص البصمة مدعوم لكنه ليس معد. + فحص البصمة + لا يمكن قراءة مفتاح البصمة. +\nاستعد كلمة السر. + لم يتعرّف على البصمة + استخدم البصمة لحفظ كلمة السر + تأريخ + مكن اشعارات الحافظة لنسخ الحقول + كيف أعد فحص البصمة للفتح السريع \? + "احفظ البصمات في " + افحص البصمة لتخزين كلمة سر قاعدة البيانات بأمان. + افحص البصمة لفتح قاعدة البيانات عند تعطيل كلمة السر. + البصمة + فحص البصمة + يسمح بفحص البصمة لفتح قاعدة البيانات + غير خط الحقول لتوضيح المحارف + افتح الملفات بالتحديد +\n + افتح الملفات تلقائيا عند تحديدها في متصفح الملفات + الوثوق بالحافظة + اسمح لكلمة السر والحقول المحمية بالدخول للحافظة + تحذير: الحافظة مشتركة بين التطبيقات وستتمكن التطبيقات الاخرى من الوصول الى المعلومات الحساسة. + تحذير: تعطيل هذه الخاصية سيمنع فتح وحفظ قاعدة البيانات. + رابط قاعدة البيانات + اسم قاعدة البيانات + وصف قاعدة البيانات + نسخة قاعدة البيانات + نص + تطبيق + أخرى + لوحة مفاتيح + تحديد المدخل مع المفتاح. + إملأ الحقول باستخدام عناصر الادخال. + أغلق قاعدة البيانات. + العودة للوحة المفاتيح الافتراضية. + إعدادات Magikeyboard + مدخل + انتهت المهلة + انتهاء المهلة لحذف مدخل الحافظة + معلومات الاشعار + أظهر إشعار عند توفر مدخل + مدخل + إمسح عند الخروج + إمسح مدخل الحافظة عند إغلاق الإشعار + مظهر + سمة لوحة المفاتيح + مفاتيح + إهتز عند اللمس + صوت عند اللمس + "إسمح بالفتح دون كلمة سر " + محمي من التعديل + افتح قاعدة البيانات للقراءة فقط افتراضيا + شاشات تعليمية + أعد عرض كل العناصر التعليمية + إعادة تعيين الشاشات التعليمية + أنشئ قاعدة بيانات + أنشئ ملف إدارة كلمات السر. + إفتح قاعدة بيانات \ No newline at end of file From 88829ca91ac9f84f5ec4f8bf54864be8f9068e6e Mon Sep 17 00:00:00 2001 From: George Date: Mon, 11 Mar 2019 10:31:12 +0000 Subject: [PATCH 080/289] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 80 +++++++++++++++------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7fa5d3b3a..be148c8a7 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -124,7 +124,7 @@ 正在保存数据库… 空格 搜索 - 数据库排序方式 + 排序方式 特殊符号 搜索 Twofish @@ -164,9 +164,9 @@ ChaCha20 AES KDF Argon2 - 选择主题 - 选择图标包 - 切换本应用中使用的图标风格 + 主题 + 图标包 + 应用程序中使用的图标包 编辑条目 密钥推导函数 找不到条目。 @@ -197,9 +197,8 @@ 从 Android 4.4.4 KitKat 开始,一些设备不再允许应用直接写入 SD 卡。 最近文件历史 记住最近使用的文件名 - 加密整个数据库时使用的算法。(密码, -\n、用户名、笔记和所有数据库中的数据将使用此算法来加密) - 将会使用随机加盐推导算法变换主密钥以生成加密算法所需的密钥。 + 数据库加密算法将用户所用的数据,包括数据库记录,数据库解锁密码、用户指纹等等。 + 将会使用随机加盐推导算法转换主密钥以生成加密算法所需的密钥。 内存使用量 密钥推导算法使用的内存(以二进制字节计)。 并行 @@ -228,7 +227,7 @@ 路径 创建新数据库 字节数 - 显示文件路径 + 文件路径 显示完整的文件路径 数据库名称 数据库描述 @@ -243,26 +242,26 @@ 默认以只读方式打开数据库 下载 贡献 - 选择各种颜色的应用主题 + 应用程序中使用的主题 每个字符串必须有一个字段名。 字段名 字段值 - 副本 %1$s + 已复制 %1$s 最低优先 ↓ 使用 Android 存储访问框架 (SAF)浏览文件(需要 KitKat 或更高) 存储访问框架 请避免在 .kbd 文件中保存含有 Latin-1 以外的其他字符的密码,因为这些字符将会被转换为同一字符。 - 历史文件夹 + 文件夹优先 底部回收站 你真的不需要密码解锁的保护吗? 你确认不使用任何的密钥吗? - 生成 %1$s + 版本 %1$s 指纹解锁功能支持,但没有配置使用。 指纹扫描 加密密码存储 不能读取指纹密钥,请重置你的密码。 未能识别的指纹 - 指纹识别错误:%1$s + 指纹识别问题:%1$s 当前数据库没有任何密码。 配置自动填充服务 打开自动填充功能,以便快速的在其他应用中填写信息 @@ -303,7 +302,7 @@ 允许剪切板保存输入的密码以及受保护的字段 警告:所有应用都可以访问剪切板,如果剪切板存在敏感数据,可能会被其他软件存储在剪切板的数据覆盖。 警告:关闭此功能可能会导致打开或者保存数据库失败。 - 打开数据库文件链接 + 打开数据库文件的链接 激活自定义键盘,填充密码和所有标识字段 选择“Magikeyboard键盘”以安全地自动填充表单。 在设备设置中激活“Magikeyboard键盘”。 @@ -313,18 +312,18 @@ 剪切板权限 通过长时间按住“空格键”以切换键盘,如果“空格键”不可用,则: 自定义用以切换键盘的按键。 - 使用输入元素填充字段。 + 使用记录信息填充字段。 锁定数据库。 再次使用默认键盘。 Magikeyboard键盘 Magikeyboard键盘(KeePass DX) Magikeyboard键盘设置 - 输入 + 单条记录 超时设置 - 超时则清空键盘输入 + 超时则清空键盘记录 通知信息 当输入可用的时候显示通知信息 - 输入 + 单条记录 %1$s 在Magikeyboard键盘中可用 %1$s 当关闭时清空输入 @@ -335,7 +334,7 @@ 按键震动 按键声音 允许空密码 - 如果没有选择密码验证,则启用“打开”按钮。 + 如果没有选择密码验证,则启用“打开”按钮 新手引导模式 高亮按钮以学习如何使用KeePass Dx 重置新手引导模式 @@ -346,15 +345,48 @@ 打开一个已有数据库 从您的文件浏览器中打开并使用一个历史数据库。 一个指向您文件的链接可用 - 您可以通过物理链接打开您的数据库(例如:file://或者content://)。 - 为您的数据库新增项目 + 您可以通过链接打开您的数据库(例如:file://或者content://)。 + 为您的数据库新增文件夹和密码数据 用户记录会帮助管理您的密码。 \n分组文件夹将组织数据库中的用户记录。 - 搜索用户记录 + 通过记录进行检索 输入标题、用户名或其他字段的内容来检索密码。 - 数据库解锁指纹 + 通过指纹解锁数据库 将您的密码链接到您扫描的指纹,以快速解锁您的数据库。 编辑记录 - 使用自定义字段编辑记录。数据可以在不同的输入字段之间引用。 + 使用自定义字段编辑记录,数据可以在不同的输入字段之间引用。 为您的记录创建一个复杂的密码。 + 在不忘记安全密码的前提下,根据表单条件轻松地产生一个复杂的密码关联您的记录。 + 添加自定义字段 + 通过填写一个您可以保护的新字段来注册一个用户不提供基础的字段。 + 解锁您的数据库 + 数据库写保护 + 在会话中改变打开方式。 +\n +\n“写保护(模式)”将阻止您对数据库任何意外的改动。 +\n“可编辑(模式)”让您可以添加、删除或者修改所有元素。 + 复制一个字段 + 已复制的字段可以粘贴到任何地方。 +\n +\n选择您喜爱的表单填充方式。 + 锁定数据库 + 您可以设置在无操作和屏幕关闭的时候快速锁定您的数据库。 + 项目排序 + 选择记录和文件夹的排序方式。 + 参与软件优化 + 帮助增加稳定性,安全性并添加更多的功能。 + 不同于大多数的密码管理软件,不论您使用哪个版本的KeePass DX,都没有广告,并且免费开源也不收集任何用户的个人信息。 + 通过购买高级版本,您将解锁全部主题样式,尤其重要的是,您会为社区项目的实现提供的帮助 + + 主题样式全部内容现在已经可用,感谢您的慷慨相助。 + 为了使这个项目继续建设并且保持免费,我门需要您的捐助/贡献 + + 这个特性目前仍在开发中,您的捐助/贡献将使这个特性在未来变得可用。 + 通过购买专业版, + 通过贡献 + + 您的标记备注,鼓励了开发人员开发新特性修复程序缺陷 + 非常感谢您的捐助/贡献。 + 我们正在努力的研发并尽快发布新特性。 + 别忘了通过安装新版本来更新您的应用程序。 \ No newline at end of file From e5902b9cf67d716dcf794d40767365b1d77b4460 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 14 Mar 2019 18:00:09 +0100 Subject: [PATCH 081/289] First refactoring pass --- .../keepass/activities/EntryEditActivity.java | 61 ++-- .../keepass/activities/GroupActivity.java | 264 ++++++++---------- .../AssignPasswordInDatabaseRunnable.java | 92 ------ .../AssignPasswordInDatabaseRunnable.kt | 94 +++++++ .../action/CreateDatabaseRunnable.java | 63 ----- .../database/action/CreateDatabaseRunnable.kt | 60 ++++ .../database/action/FileOnFinishRunnable.java | 45 --- .../database/action/LoadDatabaseRunnable.java | 139 --------- .../database/action/LoadDatabaseRunnable.kt | 121 ++++++++ .../database/action/OnFinishRunnable.java | 104 ------- .../database/action/ProgressDialogRunnable.kt | 28 ++ .../database/action/RunnableOnFinish.java | 53 ---- .../SaveDatabaseProgressDialogRunnable.kt | 57 ++++ .../database/action/SaveDatabaseRunnable.java | 87 ------ .../database/action/SaveDatabaseRunnable.kt | 59 ++++ .../node/ActionNodeDatabaseRunnable.java | 33 --- .../action/node/ActionNodeDatabaseRunnable.kt | 46 +++ .../action/node/AddEntryRunnable.java | 56 ---- .../database/action/node/AddEntryRunnable.kt | 44 +++ .../action/node/AddGroupRunnable.java | 56 ---- .../database/action/node/AddGroupRunnable.kt | 44 +++ .../node/AfterActionNodeFinishRunnable.kt | 36 +++ .../action/node/AfterActionNodeOnFinish.java | 36 --- .../action/node/CopyEntryRunnable.java | 75 ----- .../database/action/node/CopyEntryRunnable.kt | 64 +++++ .../action/node/DeleteEntryRunnable.java | 73 ----- .../action/node/DeleteEntryRunnable.kt | 61 ++++ .../action/node/DeleteGroupRunnable.java | 98 ++++--- .../action/node/MoveEntryRunnable.java | 77 ----- .../database/action/node/MoveEntryRunnable.kt | 64 +++++ .../action/node/MoveGroupRunnable.java | 86 ------ .../database/action/node/MoveGroupRunnable.kt | 72 +++++ .../action/node/UpdateEntryRunnable.java | 63 ----- .../action/node/UpdateEntryRunnable.kt | 51 ++++ .../action/node/UpdateGroupRunnable.java | 64 ----- .../action/node/UpdateGroupRunnable.kt | 51 ++++ .../AssignMasterKeyDialogFragment.java | 244 ---------------- .../dialogs/AssignMasterKeyDialogFragment.kt | 236 ++++++++++++++++ .../dialogs/PasswordEncodingDialogHelper.java | 57 ---- .../dialogs/PasswordEncodingDialogHelper.kt | 44 +++ .../fileselect/FileSelectActivity.java | 127 ++++----- .../keepass/fileselect/RecentFileHistory.java | 14 +- .../password/AssignPasswordHelper.java | 116 -------- .../keepass/password/PasswordActivity.java | 66 ++--- .../settings/NestedSettingsFragment.java | 14 +- ...aseSavePreferenceDialogFragmentCompat.java | 66 ----- ...riptionPreferenceDialogFragmentCompat.java | 32 +-- ...gorithmPreferenceDialogFragmentCompat.java | 38 ++- ...ivationPreferenceDialogFragmentCompat.java | 38 ++- ...aseNamePreferenceDialogFragmentCompat.java | 54 ++-- ...abaseSavePreferenceDialogFragmentCompat.kt | 55 ++++ ...aseSavePreferenceDialogFragmentCompat.java | 2 +- .../InputPreferenceDialogFragmentCompat.java | 2 +- ...ryUsagePreferenceDialogFragmentCompat.java | 30 +- ...llelismPreferenceDialogFragmentCompat.java | 30 +- .../RoundsPreferenceDialogFragmentCompat.java | 32 +-- .../adapter/ListRadioItemAdapter.java | 2 +- .../adapter/ListRadioViewHolder.java | 2 +- .../kunzisoft/keepass/tasks/ActionRunnable.kt | 84 ++++++ .../tasks/ProgressTaskDialogFragment.java | 129 --------- .../tasks/ProgressTaskDialogFragment.kt | 132 +++++++++ ...aveDatabaseProgressTaskDialogFragment.java | 16 -- .../kunzisoft/keepass/tasks/UIToastTask.java | 43 --- .../tasks/UpdateProgressTaskStatus.java | 58 ---- .../keepass/timeout/ClipboardHelper.java | 158 ----------- .../keepass/timeout/ClipboardHelper.kt | 144 ++++++++++ 66 files changed, 2057 insertions(+), 2585 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/FileOnFinishRunnable.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/OnFinishRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/RunnableOnFinish.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeOnFinish.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/password/AssignPasswordHelper.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/DatabaseDescriptionPreferenceDialogFragmentCompat.java (69%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java (72%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java (78%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/DatabaseNamePreferenceDialogFragmentCompat.java (60%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/InputDatabaseSavePreferenceDialogFragmentCompat.java (95%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/InputPreferenceDialogFragmentCompat.java (97%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/MemoryUsagePreferenceDialogFragmentCompat.java (74%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/ParallelismPreferenceDialogFragmentCompat.java (74%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/RoundsPreferenceDialogFragmentCompat.java (72%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/adapter/ListRadioItemAdapter.java (98%) rename app/src/main/java/com/kunzisoft/keepass/settings/{preferenceDialogFragment => preferencedialogfragment}/adapter/ListRadioViewHolder.java (94%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/tasks/SaveDatabaseProgressTaskDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/tasks/UIToastTask.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/tasks/UpdateProgressTaskStatus.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 6eabf178a..a5d44aee7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -40,6 +40,7 @@ import android.widget.Toast; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.database.Database; import com.kunzisoft.keepass.database.PwDatabase; @@ -48,27 +49,24 @@ import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwGroup; import com.kunzisoft.keepass.database.PwGroupId; import com.kunzisoft.keepass.database.PwIconStandard; -import com.kunzisoft.keepass.database.PwNode; -import com.kunzisoft.keepass.database.action.RunnableOnFinish; +import com.kunzisoft.keepass.tasks.ActionRunnable; +import com.kunzisoft.keepass.database.action.node.ActionNodeValues; import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; -import com.kunzisoft.keepass.database.action.node.AfterActionNodeOnFinish; +import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; -import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; -import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.Types; import com.kunzisoft.keepass.utils.Util; import com.kunzisoft.keepass.view.EntryEditCustomField; -import java.util.UUID; +import org.jetbrains.annotations.NotNull; -import javax.annotation.Nullable; +import java.util.UUID; import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD; @@ -264,19 +262,29 @@ public class EntryEditActivity extends LockingHideActivity mCallbackNewEntry = populateNewEntry(); // Open a progress dialog and save entry - AfterActionNodeOnFinish onFinish = new AfterSave(); - EntryEditActivity act = EntryEditActivity.this; - RunnableOnFinish task; + ActionRunnable task; + AfterActionNodeFinishRunnable afterActionNodeFinishRunnable = + new AfterActionNodeFinishRunnable() { + @Override + public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { + if (actionNodeValues.getSuccess()) + finish(); + } + }; if ( mIsNew ) { - task = new AddEntryRunnable(act, database, mCallbackNewEntry, onFinish); + task = new AddEntryRunnable(EntryEditActivity.this, + database, + mCallbackNewEntry, + afterActionNodeFinishRunnable, + !getReadOnly()); } else { - task = new UpdateEntryRunnable(act, database, mEntry, mCallbackNewEntry, onFinish); + task = new UpdateEntryRunnable(EntryEditActivity.this, + database, + mEntry, + mCallbackNewEntry, + afterActionNodeFinishRunnable, + !getReadOnly()); } - task.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); new Thread(task).start(); } @@ -582,21 +590,4 @@ public class EntryEditActivity extends LockingHideActivity Log.e(TAG, "Cant add entry as result", e); } } - - private final class AfterSave extends AfterActionNodeOnFinish { - - @Override - public void run(@Nullable PwNode oldNode, @Nullable PwNode newNode) { - runOnUiThread(() -> { - if ( mSuccess ) { - finish(); - } else { - displayMessage(EntryEditActivity.this); - } - - SaveDatabaseProgressTaskDialogFragment.stop(EntryEditActivity.this); - }); - } - } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 995aff137..b61f07ac9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -69,8 +69,11 @@ import com.kunzisoft.keepass.database.PwIcon; import com.kunzisoft.keepass.database.PwIconStandard; import com.kunzisoft.keepass.database.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; +import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; +import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; +import com.kunzisoft.keepass.database.action.node.ActionNodeValues; import com.kunzisoft.keepass.database.action.node.AddGroupRunnable; -import com.kunzisoft.keepass.database.action.node.AfterActionNodeOnFinish; +import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; import com.kunzisoft.keepass.database.action.node.CopyEntryRunnable; import com.kunzisoft.keepass.database.action.node.DeleteEntryRunnable; import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; @@ -80,22 +83,21 @@ import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; +import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; import com.kunzisoft.keepass.dialogs.ReadOnlyDialog; import com.kunzisoft.keepass.dialogs.SortDialogFragment; import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.magikeyboard.MagikIME; -import com.kunzisoft.keepass.password.AssignPasswordHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; -import com.kunzisoft.keepass.tasks.UIToastTask; -import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.view.AddNodeButtonView; import net.cachapa.expandablelayout.ExpandableLayout; +import org.jetbrains.annotations.NotNull; + public class GroupActivity extends LockingActivity implements GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener, @@ -239,8 +241,11 @@ public class GroupActivity extends LockingActivity } } - // TODO NULL getRootGroup() on null reference - rootGroup = database.getPwDatabase().getRootGroup(); + try { + rootGroup = database.getPwDatabase().getRootGroup(); + } catch (NullPointerException e) { + Log.e(TAG, "Unable to get rootGroup"); + } mCurrentGroup = retrieveCurrentGroup(getIntent(), savedInstanceState); currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); @@ -368,7 +373,7 @@ public class GroupActivity extends LockingActivity super.onSaveInstanceState(outState); } - protected PwGroup retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { + protected @Nullable PwGroup retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { // If it's a search if ( Intent.ACTION_SEARCH.equals(intent.getAction()) ) { @@ -457,7 +462,7 @@ public class GroupActivity extends LockingActivity boolean addGroupEnabled = !getReadOnly() && !currentGroupIsASearch; boolean addEntryEnabled = !getReadOnly() && !currentGroupIsASearch; if (mCurrentGroup != null) { - boolean isRoot = (mCurrentGroup == rootGroup); + boolean isRoot = (mCurrentGroup != null && mCurrentGroup == rootGroup); if (!mCurrentGroup.allowAddEntryIfIsRoot()) addEntryEnabled = !isRoot && addEntryEnabled; if (isRoot) { @@ -579,14 +584,13 @@ public class GroupActivity extends LockingActivity } private void copyEntry(PwEntry entryToCopy, PwGroup newParent) { - CopyEntryRunnable task = new CopyEntryRunnable(this, App.getDB(), entryToCopy, newParent, - new AfterAddNode()); - task.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); - new Thread(task).start(); + new Thread(new CopyEntryRunnable(this, + App.getDB(), + entryToCopy, + newParent, + new AfterAddNodeRunnable(), + !getReadOnly()) + ).start(); } @Override @@ -622,33 +626,25 @@ public class GroupActivity extends LockingActivity } private void moveGroup(PwGroup groupToMove, PwGroup newParent) { - MoveGroupRunnable task = new MoveGroupRunnable( - this, - App.getDB(), - groupToMove, - newParent, - new AfterAddNode()); - task.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); - new Thread(task).start(); + new Thread(new MoveGroupRunnable( + this, + App.getDB(), + groupToMove, + newParent, + new AfterAddNodeRunnable(), + !getReadOnly()) + ).start(); } private void moveEntry(PwEntry entryToMove, PwGroup newParent) { - MoveEntryRunnable task = new MoveEntryRunnable( - this, - App.getDB(), - entryToMove, - newParent, - new AfterAddNode()); - task.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); - new Thread(task).start(); + new Thread(new MoveEntryRunnable( + this, + App.getDB(), + entryToMove, + newParent, + new AfterAddNodeRunnable(), + !getReadOnly()) + ).start(); } @Override @@ -666,31 +662,23 @@ public class GroupActivity extends LockingActivity private void deleteGroup(PwGroup group) { //TODO Verify trash recycle bin - DeleteGroupRunnable task = new DeleteGroupRunnable( - this, - App.getDB(), - group, - new AfterDeleteNode()); - task.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); - new Thread(task).start(); + new Thread(new DeleteGroupRunnable( + this, + App.getDB(), + group, + new AfterDeleteNodeRunnable(), + !getReadOnly()) + ).start(); } private void deleteEntry(PwEntry entry) { - DeleteEntryRunnable task = new DeleteEntryRunnable( - this, - App.getDB(), - entry, - new AfterDeleteNode()); - task.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); - new Thread(task).start(); + new Thread(new DeleteEntryRunnable( + this, + App.getDB(), + entry, + new AfterDeleteNodeRunnable(), + !getReadOnly()) + ).start(); } @Override @@ -982,16 +970,12 @@ public class GroupActivity extends LockingActivity newGroup.setIconStandard(iconStandard); // If group created save it in the database - AddGroupRunnable addGroupRunnable = new AddGroupRunnable(this, - App.getDB(), - newGroup, - new AfterAddNode()); - addGroupRunnable.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); - new Thread(addGroupRunnable).start(); + new Thread(new AddGroupRunnable(this, + App.getDB(), + newGroup, + new AfterAddNodeRunnable(), + !getReadOnly()) + ).start(); break; case UPDATE: @@ -1012,95 +996,74 @@ public class GroupActivity extends LockingActivity listNodesFragment.removeNode(oldGroupToUpdate); // If group updated save it in the database - UpdateGroupRunnable updateGroupRunnable = new UpdateGroupRunnable(this, - App.getDB(), - oldGroupToUpdate, - updateGroup, - new AfterUpdateNode()); - updateGroupRunnable.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - SaveDatabaseProgressTaskDialogFragment.start( - getSupportFragmentManager()) - )); - new Thread(updateGroupRunnable).start(); + new Thread(new UpdateGroupRunnable(this, + App.getDB(), + oldGroupToUpdate, + updateGroup, + new AfterUpdateNodeRunnable(), + !getReadOnly()) + ).start(); } break; } } - class AfterAddNode extends AfterActionNodeOnFinish { + class AfterAddNodeRunnable extends AfterActionNodeFinishRunnable { @Override - public void run(PwNode oldNode, PwNode newNode) { - super.run(); - + public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { runOnUiThread(() -> { - if (mSuccess) { + if (actionNodeValues.getSuccess()) { if (listNodesFragment != null) - listNodesFragment.addNode(newNode); - } else { - displayMessage(GroupActivity.this); + listNodesFragment.addNode(actionNodeValues.getNewNode()); } + }); + } + } - SaveDatabaseProgressTaskDialogFragment.stop(GroupActivity.this); + class AfterUpdateNodeRunnable extends AfterActionNodeFinishRunnable { + + @Override + public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { + runOnUiThread(() -> { + if (actionNodeValues.getSuccess()) { + if (listNodesFragment != null) + listNodesFragment.updateNode(actionNodeValues.getOldNode(), actionNodeValues.getNewNode()); + } }); } } - class AfterUpdateNode extends AfterActionNodeOnFinish { + class AfterDeleteNodeRunnable extends AfterActionNodeFinishRunnable { @Override - public void run(PwNode oldNode, PwNode newNode) { - super.run(); - + public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { runOnUiThread(() -> { - if (mSuccess) { - if (listNodesFragment != null) - listNodesFragment.updateNode(oldNode, newNode); - } else { - displayMessage(GroupActivity.this); - } - - SaveDatabaseProgressTaskDialogFragment.stop(GroupActivity.this); - }); - } - } - - class AfterDeleteNode extends AfterActionNodeOnFinish { - - @Override - public void run(PwNode oldNode, PwNode newNode) { - super.run(); - - runOnUiThread(() -> { - if ( mSuccess) { + if (actionNodeValues.getSuccess()) { if (listNodesFragment != null) - listNodesFragment.removeNode(oldNode); + listNodesFragment.removeNode(actionNodeValues.getOldNode()); - PwGroup parent = oldNode.getParent(); - Database db = App.getDB(); - PwDatabase database = db.getPwDatabase(); - if (db.isRecycleBinAvailable() && - db.isRecycleBinEnabled()) { - PwGroup recycleBin = database.getRecycleBin(); - // Add trash if it doesn't exists - if (parent.equals(recycleBin) - && mCurrentGroup != null - && mCurrentGroup.getParent() == null - && !mCurrentGroup.equals(recycleBin)) { + if (actionNodeValues.getOldNode() != null) { + PwGroup parent = actionNodeValues.getOldNode().getParent(); + Database db = App.getDB(); + PwDatabase database = db.getPwDatabase(); + if (db.isRecycleBinAvailable() && + db.isRecycleBinEnabled()) { + PwGroup recycleBin = database.getRecycleBin(); + // Add trash if it doesn't exists + if (parent.equals(recycleBin) + && mCurrentGroup != null + && mCurrentGroup.getParent() == null + && !mCurrentGroup.equals(recycleBin)) { - if (listNodesFragment != null) - listNodesFragment.addNode(parent); - } - } - } else { - mHandler.post(new UIToastTask(GroupActivity.this, "Unrecoverable error: " + mMessage)); - finish(); + if (listNodesFragment != null) + listNodesFragment.addNode(parent); + } + } + } } - - SaveDatabaseProgressTaskDialogFragment.stop(GroupActivity.this); }); } } @@ -1139,10 +1102,25 @@ public class GroupActivity extends LockingActivity boolean masterPasswordChecked, String masterPassword, boolean keyFileChecked, Uri keyFile) { - AssignPasswordHelper assignPasswordHelper = - new AssignPasswordHelper(this, - masterPasswordChecked, masterPassword, keyFileChecked, keyFile); - assignPasswordHelper.assignPasswordInDatabase(null); + Thread taskThread = new Thread(new ProgressDialogRunnable(this, + R.string.saving_database, + progressTaskUpdater -> { + return new AssignPasswordInDatabaseRunnable(GroupActivity.this, + database, + masterPasswordChecked, + masterPassword, + keyFileChecked, + keyFile, + true); // TODO save + } + )); + // Show the progress dialog now or after dialog confirmation + if (database.getPwDatabase().validatePasswordEncoding(masterPassword)) { + taskThread.start(); + } else { + new PasswordEncodingDialogHelper() + .show(this, (dialog, which) -> taskThread.start()); + } } @Override @@ -1198,7 +1176,7 @@ public class GroupActivity extends LockingActivity public void onBackPressed() { // Normal way when we are not in root - if (!rootGroup.equals(mCurrentGroup)) + if (rootGroup!= null && !rootGroup.equals(mCurrentGroup)) super.onBackPressed(); // Else lock if needed else { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java deleted file mode 100644 index 7bddca7d3..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action; - -import android.content.Context; -import android.net.Uri; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; -import com.kunzisoft.keepass.utils.UriUtil; - -import java.io.IOException; -import java.io.InputStream; - -public class AssignPasswordInDatabaseRunnable extends SaveDatabaseRunnable { - - private String mPassword; - private Uri mKeyfile; - private byte[] mBackupKey; - - public AssignPasswordInDatabaseRunnable(Context ctx, Database db, String password, Uri keyfile, OnFinishRunnable finish) { - this(ctx, db, password, keyfile, finish, false); - } - - public AssignPasswordInDatabaseRunnable(Context ctx, Database db, String password, Uri keyfile, OnFinishRunnable finish, boolean dontSave) { - super(ctx, db, finish, dontSave); - - this.mPassword = password; - this.mKeyfile = keyfile; - } - - @Override - public void run() { - PwDatabase pm = mDatabase.getPwDatabase(); - - mBackupKey = new byte[pm.getMasterKey().length]; - System.arraycopy(pm.getMasterKey(), 0, mBackupKey, 0, mBackupKey.length); - - // Set key - try { - InputStream is = UriUtil.getUriInputStream(mContext, mKeyfile); - pm.retrieveMasterKey(mPassword, is); - // Save Database - super.run(); - } catch (InvalidKeyFileException|IOException e) { - erase(mBackupKey); - finish(false, e.getMessage()); - // run without save database - mFinish.run(); - } - - } - - @Override - protected void onFinish(boolean success, String message) { - if (!success) { - // Erase the current master key - erase(mDatabase.getPwDatabase().getMasterKey()); - mDatabase.getPwDatabase().setMasterKey(mBackupKey); - } - } - - /** - * Overwrite the array as soon as we don't need it to avoid keeping the extra data in memory - */ - private void erase(byte[] array) { - if ( array == null ) return; - - for ( int i = 0; i < array.length; i++ ) { - array[i] = 0; - } - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt new file mode 100644 index 000000000..e5329e8ec --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action + +import android.content.Context +import android.net.Uri +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.exception.InvalidKeyFileException +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.utils.UriUtil +import java.io.IOException + +class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( + ctx: Context, + db: Database, + withMasterPassword: Boolean, + masterPassword: String?, + withKeyFile: Boolean, + keyfile: Uri?, + actionRunnable: ActionRunnable? = null, + save: Boolean) + : SaveDatabaseRunnable(ctx, db, actionRunnable, save) { + + private var mMasterPassword: String? = null + private var mKeyfile: Uri? = null + + private var mBackupKey: ByteArray? = null + + init { + if (withMasterPassword) + this.mMasterPassword = masterPassword + if (withKeyFile) + this.mKeyfile = keyfile + } + + override fun run() { + // Set key + try { + val pm = database.pwDatabase + // TODO move master key methods + mBackupKey = ByteArray(pm.getMasterKey().size) + System.arraycopy(pm.getMasterKey(), 0, mBackupKey!!, 0, mBackupKey!!.size) + + val uriInputStream = UriUtil.getUriInputStream(context, mKeyfile) + pm.retrieveMasterKey(mMasterPassword, uriInputStream) + // To save the database + super.run() + finishRun(true) + } catch (e: InvalidKeyFileException) { + erase(mBackupKey) + finishRun(false, e.message) + } catch (e: IOException) { + erase(mBackupKey) + finishRun(false, e.message) + } + } + + override fun onFinishRun(isSuccess: Boolean, message: String?) { + if (!isSuccess) { + // Erase the current master key + erase(database.pwDatabase.getMasterKey()) + database.pwDatabase.setMasterKey(mBackupKey) + } + + super.onFinishRun(isSuccess, message) + } + + /** + * Overwrite the array as soon as we don't need it to avoid keeping the extra data in memory + */ + private fun erase(array: ByteArray?) { + if (array == null) return + for (i in array.indices) { + array[i] = 0 + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java deleted file mode 100644 index acd6cd5f7..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action; - -import android.content.Context; - -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.utils.UriUtil; - -public class CreateDatabaseRunnable extends RunnableOnFinish { - - private Context mContext; - private boolean mDontSave; - private String mFilename; - - public CreateDatabaseRunnable(Context mContext, String filename, OnFinishRunnable finish, boolean dontSave) { - super(finish); - - this.mContext = mContext; - this.mDontSave = dontSave; - this.mFilename = filename; - } - - @Override - public void run() { - // Create new database record - Database db = new Database(); - App.setDB(db); - - PwDatabase pm = PwDatabase.getNewDBInstance(mFilename); - pm.initNew(mFilename); - - // Set Database state - db.setPwDatabase(pm); - db.setUri(UriUtil.parseDefaultFile(mFilename)); - db.loaded = true; - - // Commit changes - SaveDatabaseRunnable save = new SaveDatabaseRunnable(mContext, db, mFinish, mDontSave); - mFinish = null; - - save.run(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt new file mode 100644 index 000000000..4dd0b9a16 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action + +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwDatabase +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.utils.UriUtil + +class CreateDatabaseRunnable(private val mFilename: String, + val onDatabaseCreate: (database: Database) -> ActionRunnable) + : ActionRunnable() { + + var database: Database? = null + + override fun run() { + try { + // Create new database record + database = Database() + App.setDB(database) + + val pm = PwDatabase.getNewDBInstance(mFilename) + pm.initNew(mFilename) + + // Set Database state + database?.pwDatabase = pm + database?.setUri(UriUtil.parseDefaultFile(mFilename)) + database?.loaded = true + + // Commit changes + database?.let { + onDatabaseCreate(it).run() + } + + finishRun(true) + } catch (e: Exception) { + finishRun(false, e.message) + } + } + + override fun onFinishRun(isSuccess: Boolean, message: String?) {} +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/FileOnFinishRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/FileOnFinishRunnable.java deleted file mode 100644 index 77da693d4..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/FileOnFinishRunnable.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action; - -import android.net.Uri; - -import java.io.Serializable; - -public class FileOnFinishRunnable extends OnFinishRunnable implements Serializable { - - private Uri mFilename = null; - protected FileOnFinishRunnable mOnFinish; - - public FileOnFinishRunnable(FileOnFinishRunnable finish) { - super(finish); - - mOnFinish = finish; - } - - public void setFilename(Uri filename) { - mFilename = filename; - } - - public Uri getFilename() { - return mFilename; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.java deleted file mode 100644 index 87c380df4..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action; - -import android.content.Context; -import android.content.SharedPreferences; -import android.net.Uri; -import android.preference.PreferenceManager; -import android.support.annotation.StringRes; -import android.util.Log; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.exception.ArcFourException; -import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException; -import com.kunzisoft.keepass.database.exception.InvalidAlgorithmException; -import com.kunzisoft.keepass.database.exception.InvalidDBException; -import com.kunzisoft.keepass.database.exception.InvalidDBSignatureException; -import com.kunzisoft.keepass.database.exception.InvalidDBVersionException; -import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; -import com.kunzisoft.keepass.database.exception.InvalidPasswordException; -import com.kunzisoft.keepass.database.exception.KeyFileEmptyException; - -import java.io.FileNotFoundException; -import java.io.IOException; - -public class LoadDatabaseRunnable extends RunnableOnFinish { - private static final String TAG = LoadDatabaseRunnable.class.getName(); - - private Context mContext; - private Database mDatabase; - private Uri mUri; - private String mPass; - private Uri mKey; - private boolean mRememberKeyfile; - - public LoadDatabaseRunnable(Context context, Database database, Uri uri, String pass, Uri key, OnFinishRunnable finish) { - super(finish); - - this.mContext = context; - this.mDatabase = database; - this.mUri = uri; - this.mPass = pass; - this.mKey = key; - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - this.mRememberKeyfile = prefs.getBoolean(context.getString(R.string.keyfile_key), context.getResources().getBoolean(R.bool.keyfile_default)); - } - - @Override - public void run() { - try { - mDatabase.loadData(mContext, mUri, mPass, mKey, mStatus); - saveFileData(mUri, mKey); - } catch (ArcFourException e) { - catchError(e, R.string.error_arc4); - return; - } catch (InvalidPasswordException e) { - catchError(e, R.string.invalid_password); - return; - } catch (ContentFileNotFoundException e) { - catchError(e, R.string.file_not_found_content); - return; - } catch (FileNotFoundException e) { - catchError(e, R.string.file_not_found); - return; - } catch (IOException e) { - if (e.getMessage().contains("Hash failed with code")) - catchError(e, R.string.error_load_database_KDF_memory, true); - else - catchError(e, R.string.error_load_database, true); - return; - } catch (KeyFileEmptyException e) { - catchError(e, R.string.keyfile_is_empty); - return; - } catch (InvalidAlgorithmException e) { - catchError(e, R.string.invalid_algorithm); - return; - } catch (InvalidKeyFileException e) { - catchError(e, R.string.keyfile_does_not_exist); - return; - } catch (InvalidDBSignatureException e) { - catchError(e, R.string.invalid_db_sig); - return; - } catch (InvalidDBVersionException e) { - catchError(e, R.string.unsupported_db_version); - return; - } catch (InvalidDBException e) { - catchError(e, R.string.error_invalid_db); - return; - } catch (OutOfMemoryError e) { - catchError(e, R.string.error_out_of_memory); - return; - } catch (Exception e) { - catchError(e, R.string.error_load_database, true); - return; - } - finish(true); - } - - private void catchError(Throwable e, @StringRes int messageId) { - catchError(e, messageId, false); - } - - private void catchError(Throwable e, @StringRes int messageId, boolean addThrowableMessage) { - String errorMessage = mContext.getString(messageId); - Log.e(TAG, errorMessage, e); - if (addThrowableMessage) - errorMessage = errorMessage + " " + e.getLocalizedMessage(); - finish(false, errorMessage); - } - - private void saveFileData(Uri uri, Uri key) { - if ( ! mRememberKeyfile ) { - key = null; - } - - App.getFileHistory().createFile(uri, key); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt new file mode 100644 index 000000000..c4ba67037 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action + +import android.content.Context +import android.net.Uri +import android.preference.PreferenceManager +import android.support.annotation.StringRes +import android.util.Log +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.exception.* +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import java.io.FileNotFoundException +import java.io.IOException + +class LoadDatabaseRunnable(private val mContext: Context, + private val mDatabase: Database, + private val mUri: Uri, + private val mPass: String?, + private val mKey: Uri?, + private val progressTaskUpdater: ProgressTaskUpdater, + nestedAction: ActionRunnable) + : ActionRunnable(nestedAction, executeNestedActionIfResultFalse = true) { + + private val mRememberKeyFile: Boolean = PreferenceManager.getDefaultSharedPreferences(mContext) + .getBoolean(mContext.getString(R.string.keyfile_key), mContext.resources.getBoolean(R.bool.keyfile_default)) + + override fun run() { + try { + mDatabase.loadData(mContext, mUri, mPass, mKey, progressTaskUpdater) + saveFileData(mUri, mKey) + finishRun(true) + } catch (e: ArcFourException) { + catchError(e, R.string.error_arc4) + return + } catch (e: InvalidPasswordException) { + catchError(e, R.string.invalid_password) + return + } catch (e: ContentFileNotFoundException) { + catchError(e, R.string.file_not_found_content) + return + } catch (e: FileNotFoundException) { + catchError(e, R.string.file_not_found) + return + } catch (e: IOException) { + var messageId = R.string.error_load_database + e.message?.let { + if (it.contains("Hash failed with code")) + messageId = R.string.error_load_database_KDF_memory + } + catchError(e, messageId, true) + return + } catch (e: KeyFileEmptyException) { + catchError(e, R.string.keyfile_is_empty) + return + } catch (e: InvalidAlgorithmException) { + catchError(e, R.string.invalid_algorithm) + return + } catch (e: InvalidKeyFileException) { + catchError(e, R.string.keyfile_does_not_exist) + return + } catch (e: InvalidDBSignatureException) { + catchError(e, R.string.invalid_db_sig) + return + } catch (e: InvalidDBVersionException) { + catchError(e, R.string.unsupported_db_version) + return + } catch (e: InvalidDBException) { + catchError(e, R.string.error_invalid_db) + return + } catch (e: OutOfMemoryError) { + catchError(e, R.string.error_out_of_memory) + return + } catch (e: Exception) { + catchError(e, R.string.error_load_database, true) + return + } + } + + private fun catchError(e: Throwable, @StringRes messageId: Int, addThrowableMessage: Boolean = false) { + var errorMessage = mContext.getString(messageId) + Log.e(TAG, errorMessage, e) + if (addThrowableMessage) + errorMessage = errorMessage + " " + e.localizedMessage + finishRun(false, errorMessage) + } + + private fun saveFileData(uri: Uri, key: Uri?) { + var keyFileUri = key + if (!mRememberKeyFile) { + keyFileUri = null + } + App.getFileHistory().createFile(uri, keyFileUri) + } + + override fun onFinishRun(isSuccess: Boolean, message: String?) {} + + companion object { + private val TAG = LoadDatabaseRunnable::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/OnFinishRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/OnFinishRunnable.java deleted file mode 100644 index 09db6bced..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/OnFinishRunnable.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action; - -import android.content.Context; -import android.os.Handler; -import android.widget.Toast; - -/** - * Callback after a task is completed. - * - * @author bpellin - * - */ -public class OnFinishRunnable implements Runnable { - protected boolean mSuccess; - protected String mMessage; - - protected OnFinishRunnable mOnFinish; - protected Handler mHandler; - - public OnFinishRunnable() { - } - - public OnFinishRunnable(Handler handler) { - mOnFinish = null; - mHandler = handler; - } - - public OnFinishRunnable(OnFinishRunnable finish, Handler handler) { - mOnFinish = finish; - mHandler = handler; - } - - public OnFinishRunnable(OnFinishRunnable finish) { - mOnFinish = finish; - mHandler = null; - } - - public void setResult(boolean success, String message) { - mSuccess = success; - mMessage = message; - } - - public void setResult(boolean success) { - mSuccess = success; - } - - public void run() { - if ( mOnFinish != null ) { - // Pass on result on call finish - mOnFinish.setResult(mSuccess, mMessage); - - if ( mHandler != null ) { - mHandler.post(mOnFinish); - } else { - mOnFinish.run(); - } - } - } - - /** - * ONLY to use in UIThread, typically in an Activity, Fragment or a Service - * @param ctx Context to show the message - */ - protected void displayMessage(Context ctx) { - if ( mMessage != null && mMessage.length() > 0 ) { - Toast.makeText(ctx, mMessage, Toast.LENGTH_LONG).show(); - } - } - - public boolean isSuccess() { - return mSuccess; - } - - public void setSuccess(boolean mSuccess) { - this.mSuccess = mSuccess; - } - - public String getMessage() { - return mMessage; - } - - public void setMessage(String mMessage) { - this.mMessage = mMessage; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogRunnable.kt new file mode 100644 index 000000000..1d482b7f4 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/ProgressDialogRunnable.kt @@ -0,0 +1,28 @@ +package com.kunzisoft.keepass.database.action + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment +import com.kunzisoft.keepass.tasks.ProgressTaskUpdater + +class ProgressDialogRunnable(val context: FragmentActivity, + private val titleId: Int, + private val actionRunnable: (ProgressTaskUpdater)-> ActionRunnable) + : ActionRunnable() { + + override fun run() { + // Show the dialog + val progressTaskUpdater: ProgressTaskUpdater = ProgressTaskDialogFragment.start( + context.supportFragmentManager, + titleId) + + // Do the action + actionRunnable.invoke(progressTaskUpdater).run() + super.run() + } + + override fun onFinishRun(isSuccess: Boolean, message: String?) { + // Remove the progress task + ProgressTaskDialogFragment.stop(context) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/RunnableOnFinish.java b/app/src/main/java/com/kunzisoft/keepass/database/action/RunnableOnFinish.java deleted file mode 100644 index aa1f32304..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/RunnableOnFinish.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action; - -import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; - - -public abstract class RunnableOnFinish implements Runnable { - - public OnFinishRunnable mFinish; - public ProgressTaskUpdater mStatus; - - public RunnableOnFinish(OnFinishRunnable finish) { - mFinish = finish; - } - - protected void finish(boolean result, String message) { - if ( mFinish != null ) { - mFinish.setResult(result, message); - mFinish.run(); - } - } - - protected void finish(boolean result) { - if ( mFinish != null ) { - mFinish.setResult(result); - mFinish.run(); - } - } - - public void setUpdateProgressTaskStatus(ProgressTaskUpdater status) { - mStatus = status; - } - - abstract public void run(); -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt new file mode 100644 index 000000000..c458aa06a --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment +import com.kunzisoft.keepass.tasks.ProgressTaskUpdater + +open class SaveDatabaseProgressDialogRunnable(var contextDatabase: FragmentActivity, + database: Database, + private val actionRunnable: ((ProgressTaskUpdater)-> ActionRunnable)?, + save: Boolean): + SaveDatabaseRunnable(contextDatabase, database, null, save) { + + override fun run() { + // Show the dialog + val progressTaskUpdater : ProgressTaskUpdater = ProgressTaskDialogFragment.start( + contextDatabase.supportFragmentManager, + R.string.saving_database) + + // Do the action if defined + actionRunnable?.invoke(progressTaskUpdater)?.run() + + // Save the database + super.run() + + // Call the finish function to close the dialog + finishRun(true) + } + + override fun onFinishRun(isSuccess: Boolean, message: String?) { + super.onFinishRun(isSuccess, message) + + // Remove the progress task + ProgressTaskDialogFragment.stop(contextDatabase) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java deleted file mode 100644 index 960ca0a00..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; -import com.kunzisoft.keepass.timeout.TimeoutHelper; - -import java.io.IOException; - -public class SaveDatabaseRunnable extends RunnableOnFinish { - - protected Context mContext; - protected Database mDatabase; - private boolean mDontSave; - - public SaveDatabaseRunnable(Context context, Database database, OnFinishRunnable finish, boolean dontSave) { - super(finish); - - this.mContext = context; - this.mDatabase = database; - this.mDontSave = dontSave; - this.mFinish = new AfterActionRunnable(finish); - } - - public SaveDatabaseRunnable(Context ctx, Database db, OnFinishRunnable finish) { - this(ctx, db, finish, false); - } - - @Override - public void run() { - TimeoutHelper.INSTANCE.temporarilyDisableTimeout(); - if (!mDontSave) { - try { - mDatabase.saveData(mContext); - } catch (IOException e) { - finish(false, e.getMessage()); - return; - } catch (PwDbOutputException e) { - // TODO: Restore - finish(false, e.getMessage()); - return; - } - } - finish(true); - } - - /** - * Method called when the database finish to save data - * @param success 'true' if save is ok, 'false' elsewhere - * @param message - */ - protected void onFinish(boolean success, String message) {} - - private class AfterActionRunnable extends OnFinishRunnable { - - AfterActionRunnable(OnFinishRunnable finish) { - super(finish); - } - - @Override - public void run() { - TimeoutHelper.INSTANCE.releaseTemporarilyDisableTimeoutAndLockIfTimeout(mContext); - onFinish(mSuccess, mMessage); - super.run(); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt new file mode 100644 index 000000000..38cb7d02a --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action + +import android.content.Context + +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.exception.PwDbOutputException +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.timeout.TimeoutHelper + +import java.io.IOException + +abstract class SaveDatabaseRunnable(protected var context: Context, + protected var database: Database, + nestedAction: ActionRunnable? = null, + private val save: Boolean) : ActionRunnable(nestedAction) { + + init { + TimeoutHelper.temporarilyDisableTimeout() + } + + override fun run() { + if (save) { + try { + database.saveData(context) + } catch (e: IOException) { + finishRun(false, e.message) + } catch (e: PwDbOutputException) { + finishRun(false, e.message) + } + } + + // Need to call super.run() in child class + } + + override fun onFinishRun(isSuccess: Boolean, message: String?) { + // Need to call super.onFinishRun(isSuccess, message) in child class + + TimeoutHelper.releaseTemporarilyDisableTimeoutAndLockIfTimeout(context) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java deleted file mode 100644 index 4a5a0870b..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwNode; -import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable; - -abstract class ActionNodeDatabaseRunnable extends SaveDatabaseRunnable { - - private AfterActionNodeOnFinish callbackRunnable; - - public ActionNodeDatabaseRunnable(Context context, Database db, AfterActionNodeOnFinish finish, boolean dontSave) { - super(context, db, finish, dontSave); - this.callbackRunnable = finish; - } - - /** - * Callback method who return the node(s) modified after an action - * - Add : @param oldNode NULL, @param newNode CreatedNode - * - Copy : @param oldNode NodeToCopy, @param newNode NodeCopied - * - Delete : @param oldNode NodeToDelete, @param NULL - * - Move : @param oldNode NULL, @param NodeToMove - * - Update : @param oldNode NodeToUpdate, @param NodeUpdated - */ - protected void callbackNodeAction(boolean success, String message, PwNode oldNode, PwNode newNode) { - if (callbackRunnable != null) { - callbackRunnable.setSuccess(success); - callbackRunnable.setMessage(message); - callbackRunnable.run(oldNode, newNode); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt new file mode 100644 index 000000000..bd548e721 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt @@ -0,0 +1,46 @@ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.action.SaveDatabaseProgressDialogRunnable + +abstract class ActionNodeDatabaseRunnable( + context: FragmentActivity, + database: Database, + private val callbackRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : SaveDatabaseProgressDialogRunnable(context, database, null, save) { + + /** + * Function do to a node action, don't implements run() if used this + */ + abstract fun nodeAction() + + override fun run() { + try { + nodeAction() + // To save the database + super.run() + finishRun(true) + } catch (e: Exception) { + finishRun(false, e.message) + } + } + + /** + * Function do get the finish node action, don't implements onFinishRun() if used this + */ + abstract fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues + + override fun onFinishRun(isSuccess: Boolean, message: String?) { + callbackRunnable?.apply { + onActionNodeFinish(nodeFinish(isSuccess, message)) + } + + if (!isSuccess) { + displayMessage(context) + } + + super.onFinishRun(isSuccess, message) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.java deleted file mode 100644 index f0f649451..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; - -public class AddEntryRunnable extends ActionNodeDatabaseRunnable { - - private PwEntry mNewEntry; - - public AddEntryRunnable(Context ctx, Database db, PwEntry entryToAdd, AfterActionNodeOnFinish finish) { - this(ctx, db, entryToAdd, finish, false); - } - - public AddEntryRunnable(Context ctx, Database db, PwEntry entryToAdd, AfterActionNodeOnFinish finish, boolean dontSave) { - super(ctx, db, finish, dontSave); - - this.mNewEntry = entryToAdd; - } - - @Override - public void run() { - mDatabase.addEntryTo(mNewEntry, mNewEntry.getParent()); - - // Commit to disk - super.run(); - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - mDatabase.removeEntryFrom(mNewEntry, mNewEntry.getParent()); - } - callbackNodeAction(success, message, null, mNewEntry); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt new file mode 100644 index 000000000..659f10007 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwEntry + +class AddEntryRunnable constructor( + context: FragmentActivity, + database: Database, + private val mNewEntry: PwEntry<*>, + finishRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { + + override fun nodeAction() { + database.addEntryTo(mNewEntry, mNewEntry.parent) + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + database.removeEntryFrom(mNewEntry, mNewEntry.parent) + } + return ActionNodeValues(isSuccess, message, null, mNewEntry) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.java deleted file mode 100644 index 6f458d2b5..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwGroup; - -public class AddGroupRunnable extends ActionNodeDatabaseRunnable { - - private PwGroup mNewGroup; - - public AddGroupRunnable(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode) { - this(ctx, db, newGroup, afterAddNode, false); - } - - public AddGroupRunnable(Context ctx, Database db, PwGroup newGroup, AfterActionNodeOnFinish afterAddNode, boolean dontSave) { - super(ctx, db, afterAddNode, dontSave); - - this.mNewGroup = newGroup; - } - - @Override - public void run() { - mDatabase.addGroupTo(mNewGroup, mNewGroup.getParent()); - - // Commit to disk - super.run(); - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - mDatabase.removeGroupFrom(mNewGroup, mNewGroup.getParent()); - } - callbackNodeAction(success, message, null, mNewGroup); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt new file mode 100644 index 000000000..0180fc517 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwGroup + +class AddGroupRunnable constructor( + context: FragmentActivity, + database: Database, + private val mNewGroup: PwGroup<*, *>, + afterAddNodeRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { + + override fun nodeAction() { + database.addGroupTo(mNewGroup, mNewGroup.parent) + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + database.removeGroupFrom(mNewGroup, mNewGroup.parent) + } + return ActionNodeValues(isSuccess, message, null, mNewGroup) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt new file mode 100644 index 000000000..a61ca1a88 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import com.kunzisoft.keepass.database.PwNode + +/** + * Callback method who return the node(s) modified after an action + * - Add : @param oldNode NULL, @param newNode CreatedNode + * - Copy : @param oldNode NodeToCopy, @param newNode NodeCopied + * - Delete : @param oldNode NodeToDelete, @param NULL + * - Move : @param oldNode NULL, @param NodeToMove + * - Update : @param oldNode NodeToUpdate, @param NodeUpdated + */ +data class ActionNodeValues(val success: Boolean, val message: String?, val oldNode: PwNode<*>?, val newNode: PwNode<*>?) + +abstract class AfterActionNodeFinishRunnable { + abstract fun onActionNodeFinish(actionNodeValues: ActionNodeValues) +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeOnFinish.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeOnFinish.java deleted file mode 100644 index b25996e43..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeOnFinish.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.os.Handler; - -import com.kunzisoft.keepass.database.PwNode; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; - -import javax.annotation.Nullable; - -public abstract class AfterActionNodeOnFinish extends OnFinishRunnable { - - public AfterActionNodeOnFinish() { - super(new Handler()); - } - - public abstract void run(@Nullable PwNode oldNode, @Nullable PwNode newNode); -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.java deleted file mode 100644 index 1488172cc..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; -import android.util.Log; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; - -public class CopyEntryRunnable extends ActionNodeDatabaseRunnable { - - private static final String TAG = CopyEntryRunnable.class.getName(); - - private PwEntry mEntryToCopy; - private PwEntry mEntryCopied; - private PwGroup mNewParent; - - public CopyEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode) { - this(context, db, oldE, newParent, afterAddNode, false); - } - - public CopyEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode, boolean dontSave) { - super(context, db, afterAddNode, dontSave); - - this.mEntryToCopy = oldE; - this.mNewParent = newParent; - } - - @Override - public void run() { - // Update entry with new values - mEntryCopied = mDatabase.copyEntry(mEntryToCopy, mNewParent); - - if (mEntryCopied != null) { - mEntryCopied.touch(true, true); - - // Commit to disk - super.run(); - } else { - Log.e(TAG, "Unable to create a copy of the entry"); - } - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - // If we fail to save, try to delete the copy - try { - mDatabase.deleteEntry(mEntryCopied); - } catch (Exception e) { - Log.i(TAG, "Unable to delete the copied entry"); - } - } - callbackNodeAction(success, message, mEntryToCopy, mEntryCopied); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt new file mode 100644 index 000000000..77da0fb0b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import android.util.Log +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwEntry +import com.kunzisoft.keepass.database.PwGroup + +class CopyEntryRunnable constructor( + context: FragmentActivity, + database: Database, + private val mEntryToCopy: PwEntry<*>, + private val mNewParent: PwGroup<*, *>, + afterAddNodeRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { + + private var mEntryCopied: PwEntry<*>? = null + + override fun nodeAction() { + // Update entry with new values + mEntryCopied = database.copyEntry(mEntryToCopy, mNewParent) + + mEntryCopied?.apply { + touch(true, true) + } ?: Log.e(TAG, "Unable to create a copy of the entry") + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + // If we fail to save, try to delete the copy + try { + database.deleteEntry(mEntryCopied) + } catch (e: Exception) { + Log.i(TAG, "Unable to delete the copied entry") + } + + } + return ActionNodeValues(isSuccess, message, mEntryToCopy, mEntryCopied) + } + + companion object { + private val TAG = CopyEntryRunnable::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.java deleted file mode 100644 index 9c9684020..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; - -public class DeleteEntryRunnable extends ActionNodeDatabaseRunnable { - - private PwEntry mEntryToDelete; - private PwGroup mParent; - private boolean mRecycle; - - public DeleteEntryRunnable(Context ctx, Database db, PwEntry entry, AfterActionNodeOnFinish finish) { - this(ctx, db, entry, finish, false); - } - - public DeleteEntryRunnable(Context ctx, Database db, PwEntry entry, AfterActionNodeOnFinish finish, boolean dontSave) { - super(ctx, db, finish, dontSave); - - this.mEntryToDelete = entry; - } - - @Override - public void run() { - mParent = mEntryToDelete.getParent(); - - // Remove Entry from parent - mRecycle = mDatabase.canRecycle(mEntryToDelete); - if (mRecycle) { - mDatabase.recycle(mEntryToDelete); - } - else { - mDatabase.deleteEntry(mEntryToDelete); - } - - // Commit database - super.run(); - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - if (mRecycle) { - mDatabase.undoRecycle(mEntryToDelete, mParent); - } - else { - mDatabase.undoDeleteEntry(mEntryToDelete, mParent); - } - } - callbackNodeAction(success, message, mEntryToDelete, null); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt new file mode 100644 index 000000000..3baff7ad7 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwEntry +import com.kunzisoft.keepass.database.PwGroup + +class DeleteEntryRunnable constructor( + context: FragmentActivity, + database: Database, + private val mEntryToDelete: PwEntry<*>, + finishRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { + + private var mParent: PwGroup<*, *>? = null + private var mRecycle: Boolean = false + + + override fun nodeAction() { + mParent = mEntryToDelete.parent + + // Remove Entry from parent + mRecycle = database.canRecycle(mEntryToDelete) + if (mRecycle) { + database.recycle(mEntryToDelete) + } else { + database.deleteEntry(mEntryToDelete) + } + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + if (mRecycle) { + database.undoRecycle(mEntryToDelete, mParent) + } else { + database.undoDeleteEntry(mEntryToDelete, mParent) + } + } + return ActionNodeValues(isSuccess, message, mEntryToDelete, null) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java index 92c13b6b7..409f90faa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java @@ -19,76 +19,74 @@ */ package com.kunzisoft.keepass.database.action.node; -import android.content.Context; +import android.support.v4.app.FragmentActivity; import com.kunzisoft.keepass.database.Database; import com.kunzisoft.keepass.database.PwEntry; import com.kunzisoft.keepass.database.PwGroup; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.util.ArrayList; import java.util.List; +// TODO Kotlinized public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { private PwGroup mGroupToDelete; private PwGroup mParent; private boolean mRecycle; - public DeleteGroupRunnable(Context ctx, Database db, PwGroup group, AfterActionNodeOnFinish finish) { - this(ctx, db, group, finish, false); - } - - public DeleteGroupRunnable(Context ctx, Database db, PwGroup group, AfterActionNodeOnFinish finish, boolean dontSave) { - super(ctx, db, finish, dontSave); + public DeleteGroupRunnable(FragmentActivity context, Database database, PwGroup group, AfterActionNodeFinishRunnable finish, boolean save) { + super(context, database, finish, save); mGroupToDelete = group; } - + @Override - public void run() { - mParent = mGroupToDelete.getParent(); + public void nodeAction() { + mParent = mGroupToDelete.getParent(); - // Remove Group from parent - mRecycle = mDatabase.canRecycle(mGroupToDelete); - if (mRecycle) { - mDatabase.recycle(mGroupToDelete); - } - else { - // TODO tests - // Remove child entries - List childEnt = new ArrayList<>(mGroupToDelete.getChildEntries()); // TODO new Methods - for ( int i = 0; i < childEnt.size(); i++ ) { - DeleteEntryRunnable task = new DeleteEntryRunnable(mContext, mDatabase, childEnt.get(i), null, true); - task.run(); - } + // Remove Group from parent + mRecycle = getDatabase().canRecycle(mGroupToDelete); + if (mRecycle) { + getDatabase().recycle(mGroupToDelete); + } + else { + // TODO tests + // Remove child entries + List childEnt = new ArrayList<>(mGroupToDelete.getChildEntries()); // TODO new Methods + for ( int i = 0; i < childEnt.size(); i++ ) { + DeleteEntryRunnable task = new DeleteEntryRunnable((FragmentActivity) getContext(), getDatabase(), childEnt.get(i), null, true); + task.run(); + } - // Remove child groups - List childGrp = new ArrayList<>(mGroupToDelete.getChildGroups()); - for ( int i = 0; i < childGrp.size(); i++ ) { - DeleteGroupRunnable task = new DeleteGroupRunnable(mContext, mDatabase, childGrp.get(i), null, true); - task.run(); - } - mDatabase.deleteGroup(mGroupToDelete); + // Remove child groups + List childGrp = new ArrayList<>(mGroupToDelete.getChildGroups()); + for ( int i = 0; i < childGrp.size(); i++ ) { + DeleteGroupRunnable task = new DeleteGroupRunnable((FragmentActivity) getContext(), getDatabase(), childGrp.get(i), null, true); + task.run(); + } + getDatabase().deleteGroup(mGroupToDelete); - // Remove from PwDatabaseV3 - // TODO ENcapsulate - mDatabase.getPwDatabase().getGroups().remove(mGroupToDelete); - } - - // Commit Database - super.run(); + // Remove from PwDatabaseV3 + // TODO ENcapsulate + getDatabase().getPwDatabase().getGroups().remove(mGroupToDelete); + } } - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - if (mRecycle) { - mDatabase.undoRecycle(mGroupToDelete, mParent); - } - else { - // Let's not bother recovering from a failure to save a deleted tree. It is too much work. - // TODO TEST pm.undoDeleteGroup(mGroup, mParent); - } - } - callbackNodeAction(success, message, mGroupToDelete, null); - } + @NotNull + @Override + public ActionNodeValues nodeFinish(boolean isSuccess, @Nullable String message) { + if ( !isSuccess ) { + if (mRecycle) { + getDatabase().undoRecycle(mGroupToDelete, mParent); + } + else { + // Let's not bother recovering from a failure to save a deleted tree. It is too much work. + // TODO TEST pm.undoDeleteGroup(mGroup, mParent); + } + } + return new ActionNodeValues(isSuccess, message, mGroupToDelete, null); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.java deleted file mode 100644 index d268fc5b2..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; -import android.util.Log; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; - -public class MoveEntryRunnable extends ActionNodeDatabaseRunnable { - - private static final String TAG = MoveEntryRunnable.class.getName(); - - private PwEntry mEntryToMove; - private PwGroup mOldParent; - private PwGroup mNewParent; - - public MoveEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode) { - this(context, db, oldE, newParent, afterAddNode, false); - } - - public MoveEntryRunnable(Context context, Database db, PwEntry oldE, PwGroup newParent, AfterActionNodeOnFinish afterAddNode, boolean dontSave) { - super(context, db, afterAddNode, dontSave); - - this.mEntryToMove = oldE; - this.mNewParent = newParent; - } - - @Override - public void run() { - // Move entry in new parent - - mOldParent = mEntryToMove.getParent(); - mDatabase.moveEntry(mEntryToMove, mNewParent); - - if (mEntryToMove != null) { - mEntryToMove.touch(true, true); - - // Commit to disk - super.run(); - } else { - Log.e(TAG, "Unable to create a copy of the entry"); - } - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - // If we fail to save, try to remove in the first place - try { - mDatabase.moveEntry(mEntryToMove, mOldParent); - } catch (Exception e) { - Log.i(TAG, "Unable to replace the entry"); - } - } - callbackNodeAction(success, message, null, mEntryToMove); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt new file mode 100644 index 000000000..cf19d29fc --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import android.util.Log +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwEntry +import com.kunzisoft.keepass.database.PwGroup + +class MoveEntryRunnable constructor( + context: FragmentActivity, + database: Database, + private val mEntryToMove: PwEntry<*>?, + private val mNewParent: PwGroup<*, *>, + afterAddNodeRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { + + private var mOldParent: PwGroup<*, *>? = null + + override fun nodeAction() { + // Move entry in new parent + mEntryToMove?.let { + mOldParent = it.parent + database.moveEntry(it, mNewParent) + it.touch(true, true) + } ?: Log.e(TAG, "Unable to create a copy of the entry") + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + // If we fail to save, try to remove in the first place + try { + database.moveEntry(mEntryToMove, mOldParent) + } catch (e: Exception) { + Log.i(TAG, "Unable to replace the entry") + } + + } + return ActionNodeValues(isSuccess, message, null, mEntryToMove) + } + + companion object { + private val TAG = MoveEntryRunnable::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.java deleted file mode 100644 index 694b0e58e..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; -import android.util.Log; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwGroup; - -public class MoveGroupRunnable extends ActionNodeDatabaseRunnable { - - private static final String TAG = MoveGroupRunnable.class.getName(); - - private PwGroup mGroupToMove; - private PwGroup mOldParent; - private PwGroup mNewParent; - - public MoveGroupRunnable(Context context, Database db, PwGroup groupToMove, PwGroup newParent, AfterActionNodeOnFinish afterAddNode) { - this(context, db, groupToMove, newParent, afterAddNode, false); - } - - public MoveGroupRunnable(Context context, Database db, PwGroup groupToMove, PwGroup newParent, AfterActionNodeOnFinish afterAddNode, boolean dontSave) { - super(context, db, afterAddNode, dontSave); - - this.mGroupToMove = groupToMove; - this.mNewParent = newParent; - } - - @Override - public void run() { - mOldParent = mGroupToMove.getParent(); - // Move group in new parent if not in the current group - if (!mGroupToMove.equals(mNewParent) - && !mNewParent.isContainedIn(mGroupToMove)) { - mDatabase.moveGroup(mGroupToMove, mNewParent); - - if (mGroupToMove != null) { - mGroupToMove.touch(true, true); - - // Commit to disk - super.run(); - } else { - Log.e(TAG, "Unable to create a copy of the group"); - } - } else { - // Only finish thread - mFinish.setResult(false); - String message = mContext.getString(R.string.error_move_folder_in_itself); - Log.e(TAG, message); - mFinish.setMessage(message); - mFinish.run(); - } - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - // If we fail to save, try to move in the first place - try { - mDatabase.moveGroup(mGroupToMove, mOldParent); - } catch (Exception e) { - Log.i(TAG, "Unable to replace the group"); - } - } - callbackNodeAction(success, message, null, mGroupToMove); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt new file mode 100644 index 000000000..ecb896a86 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import android.util.Log +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwGroup + +class MoveGroupRunnable constructor( + context: FragmentActivity, + database: Database, + private val mGroupToMove: PwGroup<*, *>?, + private val mNewParent: PwGroup<*, *>, + afterAddNodeRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { + + private var mOldParent: PwGroup<*, *>? = null + + override fun nodeAction() { + mGroupToMove?.let { + mOldParent = it.parent + // Move group in new parent if not in the current group + if (mGroupToMove != mNewParent && !mNewParent.isContainedIn(mGroupToMove)) { + database.moveGroup(mGroupToMove, mNewParent) + mGroupToMove.touch(true, true) + finishRun(true) + } else { + // Only finish thread + val message = context.getString(R.string.error_move_folder_in_itself) + Log.e(TAG, message) + finishRun(false, message) + } + } ?: Log.e(TAG, "Unable to create a copy of the group") + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + // If we fail to save, try to move in the first place + try { + database.moveGroup(mGroupToMove, mOldParent) + } catch (e: Exception) { + Log.i(TAG, "Unable to replace the group") + } + + } + return ActionNodeValues(isSuccess, message, null, mGroupToMove) + } + + companion object { + private val TAG = MoveGroupRunnable::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.java deleted file mode 100644 index 2ed6ca660..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; - -public class UpdateEntryRunnable extends ActionNodeDatabaseRunnable { - - private PwEntry mOldEntry; - private PwEntry mNewEntry; - private PwEntry mBackupEntry; - - public UpdateEntryRunnable(Context ctx, Database db, PwEntry oldE, PwEntry newE, AfterActionNodeOnFinish finish) { - this(ctx, db, oldE, newE, finish, false); - } - - public UpdateEntryRunnable(Context ctx, Database db, PwEntry oldE, PwEntry newE, AfterActionNodeOnFinish finish, boolean dontSave) { - super(ctx, db, finish, dontSave); - - this.mOldEntry = oldE; - this.mNewEntry = newE; - // Keep backup of original values in case save fails - this.mBackupEntry = mOldEntry.clone(); - } - - @Override - public void run() { - // Update entry with new values - mDatabase.updateEntry(mOldEntry, mNewEntry); - mOldEntry.touch(true, true); - - super.run(); - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - // If we fail to save, back out changes to global structure - mDatabase.updateEntry(mOldEntry, mBackupEntry); - } - callbackNodeAction(success, message, mOldEntry, mNewEntry); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt new file mode 100644 index 000000000..24b5e3878 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwEntry + +class UpdateEntryRunnable constructor( + context: FragmentActivity, + database: Database, + private val mOldEntry: PwEntry<*>, + private val mNewEntry: PwEntry<*>, + finishRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { + + // Keep backup of original values in case save fails + private val mBackupEntry: PwEntry<*> = mOldEntry.clone() + + override fun nodeAction() { + // Update entry with new values + database.updateEntry(mOldEntry, mNewEntry) + mOldEntry.touch(true, true) + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + // If we fail to save, back out changes to global structure + database.updateEntry(mOldEntry, mBackupEntry) + } + return ActionNodeValues(isSuccess, message, mOldEntry, mNewEntry) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.java deleted file mode 100644 index 66ee2a998..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.content.Context; - -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwGroup; - -public class UpdateGroupRunnable extends ActionNodeDatabaseRunnable { - - private PwGroup mOldGroup; - private PwGroup mNewGroup; - private PwGroup mBackupGroup; - - public UpdateGroupRunnable(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish) { - this(ctx, db, oldGroup, newGroup, finish, false); - } - - public UpdateGroupRunnable(Context ctx, Database db, PwGroup oldGroup, PwGroup newGroup, AfterActionNodeOnFinish finish, boolean dontSave) { - super(ctx, db, finish, dontSave); - - this.mOldGroup = oldGroup; - this.mNewGroup = newGroup; - // Keep backup of original values in case save fails - this.mBackupGroup = mOldGroup.clone(); - } - - @Override - public void run() { - // Update group with new values - mDatabase.updateGroup(mOldGroup, mNewGroup); - mOldGroup.touch(true, true); - - // Commit to disk - super.run(); - } - - @Override - protected void onFinish(boolean success, String message) { - if ( !success ) { - // If we fail to save, back out changes to global structure - mDatabase.updateGroup(mOldGroup, mBackupGroup); - } - callbackNodeAction(success, message, mOldGroup, mNewGroup); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt new file mode 100644 index 000000000..94e011c80 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.PwGroup + +class UpdateGroupRunnable constructor( + context: FragmentActivity, + database: Database, + private val mOldGroup: PwGroup<*, *>, + private val mNewGroup: PwGroup<*, *>, + finishRunnable: AfterActionNodeFinishRunnable?, + save: Boolean) + : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { + + // Keep backup of original values in case save fails + private val mBackupGroup: PwGroup<*, *> = mOldGroup.clone() + + override fun nodeAction() { + // Update group with new values + database.updateGroup(mOldGroup, mNewGroup) + mOldGroup.touch(true, true) + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + // If we fail to save, back out changes to global structure + database.updateGroup(mOldGroup, mBackupGroup) + } + return ActionNodeValues(isSuccess, message, mOldGroup, mNewGroup) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.java deleted file mode 100644 index c0ad76fd1..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.dialogs; - -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; -import android.support.v7.app.AlertDialog; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.TextView; -import android.widget.Toast; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.fileselect.KeyFileHelper; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.UriUtil; - -public class AssignMasterKeyDialogFragment extends DialogFragment { - - private String masterPassword; - private Uri mKeyfile; - - private View rootView; - private CompoundButton passwordCheckBox; - private TextView passView; - private TextView passConfView; - private CompoundButton keyfileCheckBox; - private TextView keyfileView; - - private AssignPasswordDialogListener mListener; - - private KeyFileHelper keyFileHelper; - - public interface AssignPasswordDialogListener { - void onAssignKeyDialogPositiveClick(boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile); - void onAssignKeyDialogNegativeClick(boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile); - } - - @Override - public void onAttach(Context activity) { - super.onAttach(activity); - try { - mListener = (AssignPasswordDialogListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() - + " must implement " + AssignPasswordDialogListener.class.getName()); - } - } - - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - LayoutInflater inflater = getActivity().getLayoutInflater(); - - rootView = inflater.inflate(R.layout.set_password, null); - builder.setView(rootView) - .setTitle(R.string.assign_master_key) - // Add action buttons - .setPositiveButton(android.R.string.ok, (dialog, id) -> { - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> { - }); - - passwordCheckBox = rootView.findViewById(R.id.password_checkbox); - passView = rootView.findViewById(R.id.pass_password); - passView.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void afterTextChanged(Editable editable) { - passwordCheckBox.setChecked(true); - } - }); - passConfView = rootView.findViewById(R.id.pass_conf_password); - - keyfileCheckBox = rootView.findViewById(R.id.keyfile_checkox); - keyfileView = rootView.findViewById(R.id.pass_keyfile); - keyfileView.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void afterTextChanged(Editable editable) { - keyfileCheckBox.setChecked(true); - } - }); - - keyFileHelper = new KeyFileHelper(this); - rootView.findViewById(R.id.browse_button) - .setOnClickListener(view -> keyFileHelper.getOpenFileOnClickViewListener().onClick(view)); - - AlertDialog dialog = builder.create(); - - dialog.setOnShowListener(dialog1 -> { - Button positiveButton = ((AlertDialog) dialog1).getButton(DialogInterface.BUTTON_POSITIVE); - positiveButton.setOnClickListener(v -> { - - masterPassword = ""; - mKeyfile = null; - - boolean error = verifyPassword() || verifyFile(); - - if (!passwordCheckBox.isChecked() && !keyfileCheckBox.isChecked()) { - error = true; - showNoKeyConfirmationDialog(); - } - - if (!error) { - mListener.onAssignKeyDialogPositiveClick( - passwordCheckBox.isChecked(), masterPassword, - keyfileCheckBox.isChecked(), mKeyfile); - dismiss(); - } - }); - Button negativeButton = ((AlertDialog) dialog1).getButton(DialogInterface.BUTTON_NEGATIVE); - negativeButton.setOnClickListener(v -> { - mListener.onAssignKeyDialogNegativeClick( - passwordCheckBox.isChecked(), masterPassword, - keyfileCheckBox.isChecked(), mKeyfile); - dismiss(); - }); - }); - - - return dialog; - } - - private boolean verifyPassword() { - boolean error = false; - if (passwordCheckBox.isChecked()) { - masterPassword = passView.getText().toString(); - String confpass = passConfView.getText().toString(); - - // Verify that passwords match - if (!masterPassword.equals(confpass)) { - error = true; - // Passwords do not match - Toast.makeText(getContext(), R.string.error_pass_match, Toast.LENGTH_LONG).show(); - } - - if (masterPassword == null || masterPassword.isEmpty()) { - error = true; - showEmptyPasswordConfirmationDialog(); - } - } - return error; - } - - private boolean verifyFile() { - boolean error = false; - if (keyfileCheckBox.isChecked()) { - Uri keyfile = UriUtil.parseDefaultFile(keyfileView.getText().toString()); - mKeyfile = keyfile; - - // Verify that a keyfile is set - if (EmptyUtils.isNullOrEmpty(keyfile)) { - error = true; - Toast.makeText(getContext(), R.string.error_nokeyfile, Toast.LENGTH_LONG).show(); - } - } - return error; - } - - private void showEmptyPasswordConfirmationDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage(R.string.warning_empty_password) - .setPositiveButton(android.R.string.ok, (dialog, id) -> { - if (!verifyFile()) { - mListener.onAssignKeyDialogPositiveClick( - passwordCheckBox.isChecked(), masterPassword, - keyfileCheckBox.isChecked(), mKeyfile); - AssignMasterKeyDialogFragment.this.dismiss(); - } - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> {}); - builder.create().show(); - } - - private void showNoKeyConfirmationDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setMessage(R.string.warning_no_encryption_key) - .setPositiveButton(android.R.string.ok, (dialog, id) -> { - mListener.onAssignKeyDialogPositiveClick( - passwordCheckBox.isChecked(), masterPassword, - keyfileCheckBox.isChecked(), mKeyfile); - AssignMasterKeyDialogFragment.this.dismiss(); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> {}); - builder.create().show(); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, - uri -> { - if(uri != null) { - Uri pathString = UriUtil.parseDefaultFile(uri.toString()); - if (pathString != null) { - keyfileCheckBox.setChecked(true); - keyfileView.setText(pathString.toString()); - } - } - }); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt new file mode 100644 index 000000000..8334b0ca8 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt @@ -0,0 +1,236 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.dialogs + +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.support.v4.app.DialogFragment +import android.support.v7.app.AlertDialog +import android.text.Editable +import android.text.TextWatcher +import android.view.View +import android.widget.CompoundButton +import android.widget.TextView +import android.widget.Toast +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.fileselect.KeyFileHelper +import com.kunzisoft.keepass.utils.EmptyUtils +import com.kunzisoft.keepass.utils.UriUtil + +class AssignMasterKeyDialogFragment : DialogFragment() { + + private var masterPassword: String? = null + private var mKeyfile: Uri? = null + + private var rootView: View? = null + private var passwordCheckBox: CompoundButton? = null + private var passView: TextView? = null + private var passConfView: TextView? = null + private var keyfileCheckBox: CompoundButton? = null + private var keyfileView: TextView? = null + + private var mListener: AssignPasswordDialogListener? = null + + private var keyFileHelper: KeyFileHelper? = null + + interface AssignPasswordDialogListener { + fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?, + keyFileChecked: Boolean, keyFile: Uri?) + + fun onAssignKeyDialogNegativeClick(masterPasswordChecked: Boolean, masterPassword: String?, + keyFileChecked: Boolean, keyFile: Uri?) + } + + override fun onAttach(activity: Context?) { + super.onAttach(activity) + try { + mListener = activity as AssignPasswordDialogListener? + } catch (e: ClassCastException) { + throw ClassCastException(activity!!.toString() + + " must implement " + AssignPasswordDialogListener::class.java.name) + } + + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + + activity?.let { notNullActivity -> + val builder = AlertDialog.Builder(notNullActivity) + val inflater = notNullActivity.layoutInflater + + rootView = inflater.inflate(R.layout.set_password, null) + builder.setView(rootView) + .setTitle(R.string.assign_master_key) + // Add action buttons + .setPositiveButton(android.R.string.ok) { _, _ -> } + .setNegativeButton(R.string.cancel) { _, _ -> } + + passwordCheckBox = rootView?.findViewById(R.id.password_checkbox) + passView = rootView?.findViewById(R.id.pass_password) + passView?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun afterTextChanged(editable: Editable) { + passwordCheckBox?.isChecked = true + } + }) + passConfView = rootView?.findViewById(R.id.pass_conf_password) + + keyfileCheckBox = rootView?.findViewById(R.id.keyfile_checkox) + keyfileView = rootView?.findViewById(R.id.pass_keyfile) + keyfileView?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun afterTextChanged(editable: Editable) { + keyfileCheckBox?.isChecked = true + } + }) + + keyFileHelper = KeyFileHelper(this) + rootView?.findViewById(R.id.browse_button)?.setOnClickListener { view -> + keyFileHelper?.openFileOnClickViewListener?.onClick(view) } + + val dialog = builder.create() + + if (passwordCheckBox != null && keyfileCheckBox!= null) { + dialog.setOnShowListener { dialog1 -> + val positiveButton = (dialog1 as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) + positiveButton.setOnClickListener { + + masterPassword = "" + mKeyfile = null + + var error = verifyPassword() || verifyFile() + + if (!passwordCheckBox!!.isChecked && !keyfileCheckBox!!.isChecked) { + error = true + showNoKeyConfirmationDialog() + } + + if (!error) { + mListener!!.onAssignKeyDialogPositiveClick( + passwordCheckBox!!.isChecked, masterPassword, + keyfileCheckBox!!.isChecked, mKeyfile) + dismiss() + } + } + val negativeButton = dialog1.getButton(DialogInterface.BUTTON_NEGATIVE) + negativeButton.setOnClickListener { + mListener!!.onAssignKeyDialogNegativeClick( + passwordCheckBox!!.isChecked, masterPassword, + keyfileCheckBox!!.isChecked, mKeyfile) + dismiss() + } + } + } + + return dialog + } + + return super.onCreateDialog(savedInstanceState) + } + + private fun verifyPassword(): Boolean { + var error = false + if (passwordCheckBox!!.isChecked) { + masterPassword = passView!!.text.toString() + val confpass = passConfView!!.text.toString() + + // Verify that passwords match + if (masterPassword != confpass) { + error = true + // Passwords do not match + Toast.makeText(context, R.string.error_pass_match, Toast.LENGTH_LONG).show() + } + + if (masterPassword == null || masterPassword!!.isEmpty()) { + error = true + showEmptyPasswordConfirmationDialog() + } + } + return error + } + + private fun verifyFile(): Boolean { + var error = false + if (keyfileCheckBox!!.isChecked) { + val keyfile = UriUtil.parseDefaultFile(keyfileView!!.text.toString()) + mKeyfile = keyfile + + // Verify that a keyfile is set + if (EmptyUtils.isNullOrEmpty(keyfile)) { + error = true + Toast.makeText(context, R.string.error_nokeyfile, Toast.LENGTH_LONG).show() + } + } + return error + } + + private fun showEmptyPasswordConfirmationDialog() { + val builder = AlertDialog.Builder(activity!!) + builder.setMessage(R.string.warning_empty_password) + .setPositiveButton(android.R.string.ok) { _, _ -> + if (!verifyFile()) { + mListener!!.onAssignKeyDialogPositiveClick( + passwordCheckBox!!.isChecked, masterPassword, + keyfileCheckBox!!.isChecked, mKeyfile) + this@AssignMasterKeyDialogFragment.dismiss() + } + } + .setNegativeButton(R.string.cancel) { _, _ -> } + builder.create().show() + } + + private fun showNoKeyConfirmationDialog() { + val builder = AlertDialog.Builder(activity!!) + builder.setMessage(R.string.warning_no_encryption_key) + .setPositiveButton(android.R.string.ok) { _, _ -> + mListener!!.onAssignKeyDialogPositiveClick( + passwordCheckBox!!.isChecked, masterPassword, + keyfileCheckBox!!.isChecked, mKeyfile) + this@AssignMasterKeyDialogFragment.dismiss() + } + .setNegativeButton(R.string.cancel) { _, _ -> } + builder.create().show() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + keyFileHelper!!.onActivityResultCallback(requestCode, resultCode, data + ) { uri -> + if (uri != null) { + val pathString = UriUtil.parseDefaultFile(uri.toString()) + if (pathString != null) { + keyfileCheckBox!!.isChecked = true + keyfileView!!.text = pathString.toString() + } + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.java deleted file mode 100644 index ac8c1d6a9..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.dialogs; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; - -import com.kunzisoft.keepass.R; - -public class PasswordEncodingDialogHelper { - private AlertDialog dialog; - - public void show(Context ctx, DialogInterface.OnClickListener onclick) { - show(ctx, onclick, false); - } - - public void show(Context ctx, DialogInterface.OnClickListener onclick, boolean showCancel) { - AlertDialog.Builder builder = new AlertDialog.Builder(ctx); - builder.setMessage(R.string.warning_password_encoding).setTitle(R.string.warning); - builder.setPositiveButton(android.R.string.ok, onclick); - - - if (showCancel) { - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.cancel(); - } - }); - - } - - dialog = builder.create(); - - dialog.show(); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.kt b/app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.kt new file mode 100644 index 000000000..ca0976b07 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/PasswordEncodingDialogHelper.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.dialogs + +import android.app.AlertDialog +import android.content.Context +import android.content.DialogInterface + +import com.kunzisoft.keepass.R + +class PasswordEncodingDialogHelper { + private var dialog: AlertDialog? = null + + @JvmOverloads + fun show(ctx: Context, onclick: DialogInterface.OnClickListener, showCancel: Boolean = false) { + val builder = AlertDialog.Builder(ctx) + builder.setMessage(R.string.warning_password_encoding).setTitle(R.string.warning) + builder.setPositiveButton(android.R.string.ok, onclick) + + if (showCancel) { + builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() } + } + + dialog = builder.create() + dialog?.show() + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java index 7466d0148..5430c444c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java @@ -48,27 +48,28 @@ import android.widget.Toast; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.activities.GroupActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; +import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable; -import com.kunzisoft.keepass.database.action.FileOnFinishRunnable; +import com.kunzisoft.keepass.tasks.ActionRunnable; +import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; -import com.kunzisoft.keepass.password.AssignPasswordHelper; import com.kunzisoft.keepass.password.PasswordActivity; -import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; -import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment; -import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.UriUtil; import net.cachapa.expandablelayout.ExpandableLayout; +import org.jetbrains.annotations.Nullable; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -101,14 +102,10 @@ public class FileSelectActivity extends StylishActivity implements private RecentFileHistory fileHistory; - // TODO Consultation Mode - private boolean consultationMode = false; - private View fileSelectExpandableButton; private ExpandableLayout fileSelectExpandable; private EditText openFileNameView; - private AssignPasswordHelper assignPasswordHelper; private Uri databaseUri; private KeyFileHelper keyFileHelper; @@ -534,33 +531,61 @@ public class FileSelectActivity extends StylishActivity implements try { String databaseFilename = databaseUri.getPath(); - // Prep an object to collect a password once the database has - // been created - FileOnFinishRunnable launchActivityOnFinish = new FileOnFinishRunnable( - new LaunchGroupActivity(databaseFilename)); - AssignPasswordOnFinish assignPasswordOnFinish = - new AssignPasswordOnFinish(launchActivityOnFinish); - - // Initialize the password helper assigner to set the password after the database creation - assignPasswordHelper = new AssignPasswordHelper(this, - masterPasswordChecked, masterPassword, keyFileChecked, keyFile); - assignPasswordHelper.setCreateProgressDialog(false); - - // Create the new database - CreateDatabaseRunnable createDBTask = new CreateDatabaseRunnable(FileSelectActivity.this, - databaseFilename, assignPasswordOnFinish, true); - createDBTask.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - ProgressTaskDialogFragment.start( - getSupportFragmentManager(), - R.string.progress_create) - )); - new Thread(createDBTask).start(); - + if (databaseFilename != null) { + // Create the new database and start prof + new Thread(new ProgressDialogRunnable(this, + R.string.progress_create, + progressTaskUpdater -> + new CreateDatabaseRunnable(databaseFilename, database -> { + // TODO store database created + return new AssignPasswordInDatabaseRunnable(FileSelectActivity.this, + database, + masterPasswordChecked, + masterPassword, + keyFileChecked, + keyFile, + new LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename)), + true // TODO get readonly + ); + }) + )).start(); + } } catch (Exception e) { String error = "Unable to create database with this password and key file"; Toast.makeText(this, error, Toast.LENGTH_LONG).show(); Log.e(TAG, error + " " + e.getMessage()); + // TODO remove + e.printStackTrace(); + } + } + + private class LaunchGroupActivityFinish extends ActionRunnable { + + private Uri fileURI; + + LaunchGroupActivityFinish(Uri fileUri) { + super(); + this.fileURI = fileUri; + } + + @Override + public void run() { + finishRun(true, null); + } + + @Override + public void onFinishRun(boolean isSuccess, @Nullable String message) { + runOnUiThread(() -> { + if (isSuccess) { + // Add database to recent files + fileHistory.createFile(fileURI); + mAdapter.notifyDataSetChanged(); + updateFileListVisibility(); + GroupActivity.launch(FileSelectActivity.this); + } else { + Log.e(TAG, "Unable to open the database"); + } + }); } } @@ -571,44 +596,6 @@ public class FileSelectActivity extends StylishActivity implements } - private class AssignPasswordOnFinish extends FileOnFinishRunnable { - - AssignPasswordOnFinish(FileOnFinishRunnable fileOnFinish) { - super(fileOnFinish); - } - - @Override - public void run() { - if (mSuccess) { - // Dont use ProgressTaskDialogFragment.stop(getSupportFragmentManager()); - // assignPasswordHelper do it - runOnUiThread(() -> assignPasswordHelper.assignPasswordInDatabase(mOnFinish)); - } - } - } - - private class LaunchGroupActivity extends FileOnFinishRunnable { - private Uri mUri; - - LaunchGroupActivity(String filename) { - super(null); - mUri = UriUtil.parseDefaultFile(filename); - } - - @Override - public void run() { - if (mSuccess) { - runOnUiThread(() -> { - // Add to recent files - fileHistory.createFile(mUri, getFilename()); - mAdapter.notifyDataSetChanged(); - updateFileListVisibility(); - GroupActivity.launch(FileSelectActivity.this); - }); - } - } - } - @Override public void onFileItemOpenListener(int itemPosition) { new OpenFileHistoryAsyncTask((fileName, keyFile) -> { diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java index 6fc1bb258..381107d80 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java @@ -125,17 +125,21 @@ public class RecentFileHistory { return db.exists(); } - public void createFile(Uri uri, Uri keyUri) { - if (!enabled || uri == null) return; + public void createFile(Uri databaseUri) { + createFile(databaseUri, null); + } + + public void createFile(Uri databaseUri, Uri keyFileUri) { + if (!enabled || databaseUri == null) return; init(); // Remove any existing instance of the same filename - deleteFile(uri, false); + deleteFile(databaseUri, false); - databases.add(0, uri.toString()); + databases.add(0, databaseUri.toString()); - String key = (keyUri == null) ? "" : keyUri.toString(); + String key = (keyFileUri == null) ? "" : keyFileUri.toString(); keyfiles.add(0, key); trimLists(); diff --git a/app/src/main/java/com/kunzisoft/keepass/password/AssignPasswordHelper.java b/app/src/main/java/com/kunzisoft/keepass/password/AssignPasswordHelper.java deleted file mode 100644 index 48e9c9535..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/password/AssignPasswordHelper.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.password; - -import android.net.Uri; -import android.support.v7.app.AppCompatActivity; -import android.widget.Toast; - -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; -import com.kunzisoft.keepass.database.action.FileOnFinishRunnable; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; -import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; -import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; - -public class AssignPasswordHelper { - - private AppCompatActivity context; - - private String masterPassword = null; - private Uri keyfile = null; - - private boolean createProgressDialog; - - public AssignPasswordHelper(AppCompatActivity context, - boolean withMasterPassword, - String masterPassword, - boolean withKeyFile, - Uri keyfile) { - this.context = context; - if (withMasterPassword) - this.masterPassword = masterPassword; - if (withKeyFile) - this.keyfile = keyfile; - - createProgressDialog = true; - } - - public void setCreateProgressDialog(boolean createProgressDialog) { - this.createProgressDialog = createProgressDialog; - } - - public void assignPasswordInDatabase(FileOnFinishRunnable fileOnFinish) { - AssignPasswordInDatabaseRunnable assignPasswordInDatabaseRunnable = new AssignPasswordInDatabaseRunnable( - context, - App.getDB(), - masterPassword, - keyfile, - new AfterSave(fileOnFinish) - ); - if (createProgressDialog) { - assignPasswordInDatabaseRunnable.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(context, - SaveDatabaseProgressTaskDialogFragment.start( - context.getSupportFragmentManager()) - )); - } - Thread taskThread = new Thread(assignPasswordInDatabaseRunnable); - - // Show the progress dialog now or after dialog confirmation - if (App.getDB().getPwDatabase().validatePasswordEncoding(masterPassword)) { - taskThread.start(); - } else { - PasswordEncodingDialogHelper dialog = new PasswordEncodingDialogHelper(); - dialog.show(context, (newDialog, which) -> taskThread.start(), true); - } - } - - private class AfterSave extends OnFinishRunnable { - private FileOnFinishRunnable mFinish; - - AfterSave(FileOnFinishRunnable finish) { - super(finish); - mFinish = finish; - } - - @Override - public void run() { - super.run(); - - context.runOnUiThread(() -> { - if ( mSuccess ) { - if ( mFinish != null ) { - mFinish.setFilename(keyfile); - } - } else { - if ( mMessage != null && mMessage.length() > 0 ) { - Toast.makeText(context, mMessage, Toast.LENGTH_LONG).show(); - } - } - - // To remove progress task - ProgressTaskDialogFragment.stop(context); - }); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 71a70094e..cb4529029 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -55,6 +55,7 @@ import android.widget.Toast; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.activities.GroupActivity; import com.kunzisoft.keepass.activities.IntentBuildLauncher; import com.kunzisoft.keepass.activities.ReadOnlyHelper; @@ -63,23 +64,23 @@ import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.compat.ClipDataCompat; import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; +import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; import com.kunzisoft.keepass.fileselect.KeyFileHelper; import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; -import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; -import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment; -import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.UriUtil; +import org.jetbrains.annotations.Nullable; + import java.io.File; import java.io.FileNotFoundException; @@ -883,43 +884,35 @@ public class PasswordActivity extends StylishActivity Database database = App.getDB(); database.closeAndClear(getApplicationContext()); - // Show the progress dialog - Handler handler = new Handler(); - AfterLoadingDatabase afterLoad = new AfterLoadingDatabase(handler, database); - LoadDatabaseRunnable databaseLoadingTask = new LoadDatabaseRunnable( - PasswordActivity.this, - database, - mDbUri, - password, - keyfile, - afterLoad); - databaseLoadingTask.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(this, - handler, - ProgressTaskDialogFragment.start( - getSupportFragmentManager(), - R.string.loading_database) - )); - new Thread(databaseLoadingTask).start(); + // Show the progress dialog and load the database + new Thread(new ProgressDialogRunnable( + this, + R.string.loading_database, + progressTaskUpdater -> new LoadDatabaseRunnable( + PasswordActivity.this, + database, + mDbUri, + password, + keyfile, + progressTaskUpdater, + new AfterLoadingDatabase(database)) + )).start(); } /** * Called after verify and try to opening the database */ - private final class AfterLoadingDatabase extends OnFinishRunnable { + private final class AfterLoadingDatabase extends ActionRunnable { - protected Database db; + protected Database database; - AfterLoadingDatabase( - Handler handler, - Database db) { - super(handler); - - this.db = db; + AfterLoadingDatabase(Database database) { + super(); + this.database = database; } @Override - public void run() { + public void onFinishRun(boolean isSuccess, @Nullable String message) { runOnUiThread(() -> { // Recheck fingerprint if error if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -927,19 +920,16 @@ public class PasswordActivity extends StylishActivity reInitWithFingerprintMode(); } - if (db.isPasswordEncodingError()) { + if (database.isPasswordEncodingError()) { PasswordEncodingDialogHelper dialog = new PasswordEncodingDialogHelper(); dialog.show(PasswordActivity.this, (dialog1, which) -> launchGroupActivity()); - } else if (mSuccess) { + } else if (isSuccess) { launchGroupActivity(); } else { - if ( mMessage != null && mMessage.length() > 0 ) { - Toast.makeText(PasswordActivity.this, mMessage, Toast.LENGTH_LONG).show(); + if ( getMessage() != null && getMessage().length() > 0 ) { + Toast.makeText(PasswordActivity.this, getMessage(), Toast.LENGTH_LONG).show(); } } - - // To remove progress task - ProgressTaskDialogFragment.stop(PasswordActivity.this); }); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index 8d2b37910..b8e64cf34 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -52,13 +52,13 @@ import com.kunzisoft.keepass.dialogs.UnderDevelopmentFeatureDialogFragment; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; import com.kunzisoft.keepass.icons.IconPackChooser; import com.kunzisoft.keepass.dialogs.KeyboardExplanationDialogFragment; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.DatabaseDescriptionPreferenceDialogFragmentCompat; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.DatabaseKeyDerivationPreferenceDialogFragmentCompat; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.DatabaseNamePreferenceDialogFragmentCompat; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.MemoryUsagePreferenceDialogFragmentCompat; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.ParallelismPreferenceDialogFragmentCompat; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.RoundsPreferenceDialogFragmentCompat; +import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDescriptionPreferenceDialogFragmentCompat; +import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat; +import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseKeyDerivationPreferenceDialogFragmentCompat; +import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseNamePreferenceDialogFragmentCompat; +import com.kunzisoft.keepass.settings.preferencedialogfragment.MemoryUsagePreferenceDialogFragmentCompat; +import com.kunzisoft.keepass.settings.preferencedialogfragment.ParallelismPreferenceDialogFragmentCompat; +import com.kunzisoft.keepass.settings.preferencedialogfragment.RoundsPreferenceDialogFragmentCompat; import com.kunzisoft.keepass.stylish.Stylish; public class NestedSettingsFragment extends PreferenceFragmentCompat diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java deleted file mode 100644 index 7f0e282be..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseSavePreferenceDialogFragmentCompat.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; - -import android.view.View; - -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; -import com.kunzisoft.keepass.tasks.UpdateProgressTaskStatus; - -public abstract class DatabaseSavePreferenceDialogFragmentCompat extends InputPreferenceDialogFragmentCompat { - - protected Database database; - - private OnFinishRunnable afterSaveDatabase; - - @Override - protected void onBindDialogView(View view) { - super.onBindDialogView(view); - - this.database = App.getDB(); - } - - @Override - public void onDialogClosed(boolean positiveResult) { - if ( positiveResult ) { - assert getActivity() != null; - - if (database != null && afterSaveDatabase != null) { - SaveDatabaseRunnable saveDatabaseRunnable = new SaveDatabaseRunnable(getActivity(), - database, - afterSaveDatabase); - saveDatabaseRunnable.setUpdateProgressTaskStatus( - new UpdateProgressTaskStatus(getContext(), - SaveDatabaseProgressTaskDialogFragment.start( - getActivity().getSupportFragmentManager()) - )); - new Thread(saveDatabaseRunnable).start(); - } - } - } - - public void setAfterSaveDatabase(OnFinishRunnable afterSaveDatabase) { - this.afterSaveDatabase = afterSaveDatabase; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java similarity index 69% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java index 5519d1015..d655418e6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.java @@ -17,15 +17,15 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.os.Bundle; -import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.view.View; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; +import com.kunzisoft.keepass.tasks.ActionRunnable; + +import org.jetbrains.annotations.Nullable; public class DatabaseDescriptionPreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat { @@ -44,7 +44,7 @@ public class DatabaseDescriptionPreferenceDialogFragmentCompat extends InputData protected void onBindDialogView(View view) { super.onBindDialogView(view); - setInputText(database.getDescription()); + setInputText(getDatabase().getDescription()); } @Override @@ -53,24 +53,23 @@ public class DatabaseDescriptionPreferenceDialogFragmentCompat extends InputData assert getContext() != null; String newDescription = getInputText(); - String oldDescription = database.getDescription(); - database.assignDescription(newDescription); + String oldDescription = getDatabase().getDescription(); + getDatabase().assignDescription(newDescription); - Handler handler = new Handler(); - setAfterSaveDatabase(new AfterDescriptionSave((AppCompatActivity) getActivity(), handler, newDescription, oldDescription)); + setAfterSaveDatabaseRunnable(new AfterDescriptionSave((AppCompatActivity) getActivity(), newDescription, oldDescription)); } super.onDialogClosed(positiveResult); } - private class AfterDescriptionSave extends OnFinishRunnable { + private class AfterDescriptionSave extends ActionRunnable { private AppCompatActivity mActivity; private String mNewDescription; private String mOldDescription; - AfterDescriptionSave(AppCompatActivity ctx, Handler handler, String newDescription, String oldDescription) { - super(handler); + AfterDescriptionSave(AppCompatActivity ctx, String newDescription, String oldDescription) { + super(); mActivity = ctx; mNewDescription = newDescription; @@ -78,22 +77,19 @@ public class DatabaseDescriptionPreferenceDialogFragmentCompat extends InputData } @Override - public void run() { + public void onFinishRun(boolean isSuccess, @Nullable String message) { if (mActivity != null) { mActivity.runOnUiThread(() -> { String descriptionToShow = mNewDescription; - if (!mSuccess) { + if (!isSuccess) { displayMessage(mActivity); - database.assignDescription(mOldDescription); + getDatabase().assignDescription(mOldDescription); } getPreference().setSummary(descriptionToShow); - SaveDatabaseProgressTaskDialogFragment.stop(mActivity); }); } - - super.run(); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java similarity index 72% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java index 72432878b..f06b12189 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java @@ -17,10 +17,9 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.os.Bundle; -import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -28,9 +27,10 @@ import android.view.View; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.adapter.ListRadioItemAdapter; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; +import com.kunzisoft.keepass.tasks.ActionRunnable; +import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter; + +import org.jetbrains.annotations.Nullable; public class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat extends DatabaseSavePreferenceDialogFragmentCompat implements ListRadioItemAdapter.RadioItemSelectedCallback { @@ -60,23 +60,22 @@ public class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat extends D encryptionAlgorithmAdapter.setRadioItemSelectedCallback(this); recyclerView.setAdapter(encryptionAlgorithmAdapter); - algorithmSelected = database.getEncryptionAlgorithm(); - encryptionAlgorithmAdapter.setItems(database.getAvailableEncryptionAlgorithms(), algorithmSelected); + algorithmSelected = getDatabase().getEncryptionAlgorithm(); + encryptionAlgorithmAdapter.setItems(getDatabase().getAvailableEncryptionAlgorithms(), algorithmSelected); } @Override public void onDialogClosed(boolean positiveResult) { if ( positiveResult - && database.allowEncryptionAlgorithmModification()) { + && getDatabase().allowEncryptionAlgorithmModification()) { assert getContext() != null; if (algorithmSelected != null) { PwEncryptionAlgorithm newAlgorithm = algorithmSelected; - PwEncryptionAlgorithm oldAlgorithm = database.getEncryptionAlgorithm(); - database.assignEncryptionAlgorithm(newAlgorithm); + PwEncryptionAlgorithm oldAlgorithm = getDatabase().getEncryptionAlgorithm(); + getDatabase().assignEncryptionAlgorithm(newAlgorithm); - Handler handler = new Handler(); - setAfterSaveDatabase(new AfterDescriptionSave((AppCompatActivity) getActivity(), handler, newAlgorithm, oldAlgorithm)); + setAfterSaveDatabaseRunnable(new AfterDescriptionSave((AppCompatActivity) getActivity(), newAlgorithm, oldAlgorithm)); } } @@ -88,14 +87,14 @@ public class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat extends D this.algorithmSelected = item; } - private class AfterDescriptionSave extends OnFinishRunnable { + private class AfterDescriptionSave extends ActionRunnable { private PwEncryptionAlgorithm mNewAlgorithm; private PwEncryptionAlgorithm mOldAlgorithm; private AppCompatActivity mActivity; - AfterDescriptionSave(AppCompatActivity activity, Handler handler, PwEncryptionAlgorithm newAlgorithm, PwEncryptionAlgorithm oldAlgorithm) { - super(handler); + AfterDescriptionSave(AppCompatActivity activity, PwEncryptionAlgorithm newAlgorithm, PwEncryptionAlgorithm oldAlgorithm) { + super(); mActivity = activity; mNewAlgorithm = newAlgorithm; @@ -103,21 +102,18 @@ public class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat extends D } @Override - public void run() { + public void onFinishRun(boolean isSuccess, @Nullable String message) { if (mActivity != null) { mActivity.runOnUiThread(() -> { PwEncryptionAlgorithm algorithmToShow = mNewAlgorithm; - if (!mSuccess) { + if (!isSuccess) { displayMessage(mActivity); - database.assignEncryptionAlgorithm(mOldAlgorithm); + getDatabase().assignEncryptionAlgorithm(mOldAlgorithm); } getPreference().setSummary(algorithmToShow.getName(mActivity.getResources())); - SaveDatabaseProgressTaskDialogFragment.stop(mActivity); }); } - - super.run(); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java similarity index 78% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java index 48ffa5e2a..ea465b20c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.java @@ -17,10 +17,9 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.os.Bundle; -import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.Preference; import android.support.v7.widget.LinearLayoutManager; @@ -29,9 +28,10 @@ import android.view.View; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.settings.preferenceDialogFragment.adapter.ListRadioItemAdapter; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; +import com.kunzisoft.keepass.tasks.ActionRunnable; +import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter; + +import org.jetbrains.annotations.Nullable; public class DatabaseKeyDerivationPreferenceDialogFragmentCompat extends DatabaseSavePreferenceDialogFragmentCompat implements ListRadioItemAdapter.RadioItemSelectedCallback { @@ -64,23 +64,22 @@ public class DatabaseKeyDerivationPreferenceDialogFragmentCompat extends Databas kdfAdapter.setRadioItemSelectedCallback(this); recyclerView.setAdapter(kdfAdapter); - kdfEngineSelected = database.getKdfEngine(); - kdfAdapter.setItems(database.getAvailableKdfEngines(), kdfEngineSelected); + kdfEngineSelected = getDatabase().getKdfEngine(); + kdfAdapter.setItems(getDatabase().getAvailableKdfEngines(), kdfEngineSelected); } @Override public void onDialogClosed(boolean positiveResult) { if ( positiveResult - && database.allowKdfModification()) { + && getDatabase().allowKdfModification()) { assert getContext() != null; if (kdfEngineSelected != null) { KdfEngine newKdfEngine = kdfEngineSelected; - KdfEngine oldKdfEngine = database.getKdfEngine(); - database.assignKdfEngine(newKdfEngine); + KdfEngine oldKdfEngine = getDatabase().getKdfEngine(); + getDatabase().assignKdfEngine(newKdfEngine); - Handler handler = new Handler(); - setAfterSaveDatabase(new AfterDescriptionSave((AppCompatActivity) getActivity(), handler, newKdfEngine, oldKdfEngine)); + setAfterSaveDatabaseRunnable(new AfterDescriptionSave((AppCompatActivity) getActivity(), newKdfEngine, oldKdfEngine)); } } @@ -104,14 +103,14 @@ public class DatabaseKeyDerivationPreferenceDialogFragmentCompat extends Databas kdfEngineSelected = item; } - private class AfterDescriptionSave extends OnFinishRunnable { + private class AfterDescriptionSave extends ActionRunnable { private KdfEngine mNewKdfEngine; private KdfEngine mOldKdfEngine; private AppCompatActivity mActivity; - AfterDescriptionSave(AppCompatActivity activity, Handler handler, KdfEngine newKdfEngine, KdfEngine oldKdfEngine) { - super(handler); + AfterDescriptionSave(AppCompatActivity activity, KdfEngine newKdfEngine, KdfEngine oldKdfEngine) { + super(); this.mActivity = activity; this.mNewKdfEngine = newKdfEngine; @@ -119,14 +118,14 @@ public class DatabaseKeyDerivationPreferenceDialogFragmentCompat extends Databas } @Override - public void run() { + public void onFinishRun(boolean isSuccess, @Nullable String message) { if (mActivity != null) { mActivity.runOnUiThread(() -> { KdfEngine kdfEngineToShow = mNewKdfEngine; - if (!mSuccess) { + if (!isSuccess) { displayMessage(mActivity); - database.assignKdfEngine(mOldKdfEngine); + getDatabase().assignKdfEngine(mOldKdfEngine); } getPreference().setSummary(kdfEngineToShow.getName(mActivity.getResources())); @@ -140,11 +139,8 @@ public class DatabaseKeyDerivationPreferenceDialogFragmentCompat extends Databas if (parallelismPreference != null) { parallelismPreference.setSummary(String.valueOf(kdfEngineToShow.getDefaultParallelism())); } - SaveDatabaseProgressTaskDialogFragment.stop(mActivity); }); } - - super.run(); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseNamePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.java similarity index 60% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseNamePreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.java index 5db6c5262..f60504328 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/DatabaseNamePreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.java @@ -17,15 +17,15 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.os.Bundle; -import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.view.View; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; +import com.kunzisoft.keepass.tasks.ActionRunnable; + +import org.jetbrains.annotations.Nullable; public class DatabaseNamePreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat { @@ -44,7 +44,7 @@ public class DatabaseNamePreferenceDialogFragmentCompat extends InputDatabaseSav protected void onBindDialogView(View view) { super.onBindDialogView(view); - setInputText(database.getName()); + setInputText(getDatabase().getName()); } @Override @@ -53,47 +53,43 @@ public class DatabaseNamePreferenceDialogFragmentCompat extends InputDatabaseSav assert getContext() != null; String newName = getInputText(); - String oldName = database.getName(); - database.assignName(newName); + String oldName = getDatabase().getName(); + getDatabase().assignName(newName); - Handler handler = new Handler(); - setAfterSaveDatabase(new AfterNameSave((AppCompatActivity) getActivity(), handler, newName, oldName)); + setAfterSaveDatabaseRunnable(new AfterNameSave((AppCompatActivity) getActivity(), newName, oldName)); } super.onDialogClosed(positiveResult); } - private class AfterNameSave extends OnFinishRunnable { + private class AfterNameSave extends ActionRunnable { private String mNewName; private String mOldName; private AppCompatActivity mActivity; - AfterNameSave(AppCompatActivity ctx, Handler handler, String newName, String oldName) { - super(handler); + AfterNameSave(AppCompatActivity ctx, String newName, String oldName) { + super(); mActivity = ctx; mNewName = newName; mOldName = oldName; } - @Override - public void run() { - if (mActivity != null) { - mActivity.runOnUiThread(() -> { - String nameToShow = mNewName; + @Override + public void onFinishRun(boolean isSuccess, @Nullable String message) { + if (mActivity != null) { + mActivity.runOnUiThread(() -> { + String nameToShow = mNewName; - if (!mSuccess) { - displayMessage(mActivity); - database.assignName(mOldName); - } + if (!isSuccess) { + displayMessage(mActivity); + getDatabase().assignName(mOldName); + } - getPreference().setSummary(nameToShow); - SaveDatabaseProgressTaskDialogFragment.stop(mActivity); - }); - } - - super.run(); - } - } + getPreference().setSummary(nameToShow); + }); + } + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt new file mode 100644 index 000000000..2b5ad2f4a --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.settings.preferencedialogfragment + +import android.view.View +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.action.SaveDatabaseProgressDialogRunnable +import com.kunzisoft.keepass.tasks.ActionRunnable + +abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialogFragmentCompat() { + + protected var database: Database? = null + + var afterSaveDatabaseRunnable: ActionRunnable? = null + + override fun onBindDialogView(view: View) { + super.onBindDialogView(view) + + this.database = App.getDB() + } + + override fun onDialogClosed(positiveResult: Boolean) { + if (positiveResult) { + activity?.let { notNullActivity -> + database?.let { notNullDatabase -> + afterSaveDatabaseRunnable?.let { runnable -> + Thread(SaveDatabaseProgressDialogRunnable( + notNullActivity, + notNullDatabase, + { runnable }, + true)).start() // TODO Read only + } + } + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/InputDatabaseSavePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/InputDatabaseSavePreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.java index 571b6c5e4..92d9b66a4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/InputDatabaseSavePreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputDatabaseSavePreferenceDialogFragmentCompat.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.view.View; import android.widget.EditText; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/InputPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/InputPreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.java index 7fb9444d6..ec26674fa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/InputPreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.support.annotation.StringRes; import android.support.v7.preference.PreferenceDialogFragmentCompat; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/MemoryUsagePreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.java similarity index 74% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/MemoryUsagePreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.java index 2c2aee14e..2c8e76085 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/MemoryUsagePreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/MemoryUsagePreferenceDialogFragmentCompat.java @@ -17,17 +17,15 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.os.Bundle; -import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; +import com.kunzisoft.keepass.tasks.ActionRunnable; public class MemoryUsagePreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat { @@ -47,7 +45,7 @@ public class MemoryUsagePreferenceDialogFragmentCompat extends InputDatabaseSave super.onBindDialogView(view); setExplanationText(R.string.memory_usage_explanation); - setInputText(database.getMemoryUsageAsString()); + setInputText(getDatabase().getMemoryUsageAsString()); } @Override @@ -68,24 +66,23 @@ public class MemoryUsagePreferenceDialogFragmentCompat extends InputDatabaseSave memoryUsage = 1; } - long oldMemoryUsage = database.getMemoryUsage(); - database.setMemoryUsage(memoryUsage); + long oldMemoryUsage = getDatabase().getMemoryUsage(); + getDatabase().setMemoryUsage(memoryUsage); - Handler handler = new Handler(); - setAfterSaveDatabase(new AfterMemorySave((AppCompatActivity) getActivity(), handler, memoryUsage, oldMemoryUsage)); + setAfterSaveDatabaseRunnable(new AfterMemorySave((AppCompatActivity) getActivity(), memoryUsage, oldMemoryUsage)); } super.onDialogClosed(positiveResult); } - private class AfterMemorySave extends OnFinishRunnable { + private class AfterMemorySave extends ActionRunnable { private long mNewMemory; private long mOldMemory; private AppCompatActivity mActivity; - AfterMemorySave(AppCompatActivity ctx, Handler handler, long newMemory, long oldMemory) { - super(handler); + AfterMemorySave(AppCompatActivity ctx, long newMemory, long oldMemory) { + super(); mActivity = ctx; mNewMemory = newMemory; @@ -93,22 +90,19 @@ public class MemoryUsagePreferenceDialogFragmentCompat extends InputDatabaseSave } @Override - public void run() { + public void onFinishRun(boolean isSuccess, String message) { if (mActivity != null) { mActivity.runOnUiThread(() -> { long memoryToShow = mNewMemory; - if (!mSuccess) { + if (!isSuccess) { displayMessage(mActivity); - database.setMemoryUsage(mOldMemory); + getDatabase().setMemoryUsage(mOldMemory); } getPreference().setSummary(String.valueOf(memoryToShow)); - SaveDatabaseProgressTaskDialogFragment.stop(mActivity); }); } - - super.run(); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/ParallelismPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.java similarity index 74% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/ParallelismPreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.java index 29fb51033..ad369e520 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/ParallelismPreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/ParallelismPreferenceDialogFragmentCompat.java @@ -17,17 +17,15 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.os.Bundle; -import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; +import com.kunzisoft.keepass.tasks.ActionRunnable; public class ParallelismPreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat { @@ -47,7 +45,7 @@ public class ParallelismPreferenceDialogFragmentCompat extends InputDatabaseSave super.onBindDialogView(view); setExplanationText(R.string.parallelism_explanation); - setInputText(database.getParallelismAsString()); + setInputText(getDatabase().getParallelismAsString()); } @Override @@ -68,24 +66,23 @@ public class ParallelismPreferenceDialogFragmentCompat extends InputDatabaseSave parallelism = 1; } - int oldParallelism = database.getParallelism(); - database.setParallelism(parallelism); + int oldParallelism = getDatabase().getParallelism(); + getDatabase().setParallelism(parallelism); - Handler handler = new Handler(); - setAfterSaveDatabase(new AfterParallelismSave((AppCompatActivity) getActivity(), handler, parallelism, oldParallelism)); + setAfterSaveDatabaseRunnable(new AfterParallelismSave((AppCompatActivity) getActivity(), parallelism, oldParallelism)); } super.onDialogClosed(positiveResult); } - private class AfterParallelismSave extends OnFinishRunnable { + private class AfterParallelismSave extends ActionRunnable { private int mNewParallelism; private int mOldParallelism; private AppCompatActivity mActivity; - AfterParallelismSave(AppCompatActivity ctx, Handler handler, int newParallelism, int oldParallelism) { - super(handler); + AfterParallelismSave(AppCompatActivity ctx, int newParallelism, int oldParallelism) { + super(); mActivity = ctx; mNewParallelism = newParallelism; @@ -93,22 +90,19 @@ public class ParallelismPreferenceDialogFragmentCompat extends InputDatabaseSave } @Override - public void run() { + public void onFinishRun(boolean isSuccess, String message) { if (mActivity != null) { mActivity.runOnUiThread(() -> { int parallelismToShow = mNewParallelism; - if (!mSuccess) { + if (!isSuccess) { displayMessage(mActivity); - database.setParallelism(mOldParallelism); + getDatabase().setParallelism(mOldParallelism); } getPreference().setSummary(String.valueOf(parallelismToShow)); - SaveDatabaseProgressTaskDialogFragment.stop(mActivity); }); } - - super.run(); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/RoundsPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.java similarity index 72% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/RoundsPreferenceDialogFragmentCompat.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.java index d27208164..723afcd77 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/RoundsPreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/RoundsPreferenceDialogFragmentCompat.java @@ -17,17 +17,15 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment; +package com.kunzisoft.keepass.settings.preferencedialogfragment; import android.os.Bundle; -import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.action.OnFinishRunnable; -import com.kunzisoft.keepass.tasks.SaveDatabaseProgressTaskDialogFragment; +import com.kunzisoft.keepass.tasks.ActionRunnable; public class RoundsPreferenceDialogFragmentCompat extends InputDatabaseSavePreferenceDialogFragmentCompat { @@ -47,7 +45,7 @@ public class RoundsPreferenceDialogFragmentCompat extends InputDatabaseSavePrefe super.onBindDialogView(view); setExplanationText(getString(R.string.rounds_explanation)); - setInputText(database.getNumberKeyEncryptionRoundsAsString()); + setInputText(getDatabase().getNumberKeyEncryptionRoundsAsString()); } @Override @@ -68,29 +66,28 @@ public class RoundsPreferenceDialogFragmentCompat extends InputDatabaseSavePrefe rounds = 1; } - long oldRounds = database.getNumberKeyEncryptionRounds(); + long oldRounds = getDatabase().getNumberKeyEncryptionRounds(); try { - database.setNumberKeyEncryptionRounds(rounds); + getDatabase().setNumberKeyEncryptionRounds(rounds); } catch (NumberFormatException e) { Toast.makeText(getContext(), R.string.error_rounds_too_large, Toast.LENGTH_LONG).show(); - database.setNumberKeyEncryptionRounds(Integer.MAX_VALUE); + getDatabase().setNumberKeyEncryptionRounds(Integer.MAX_VALUE); } - Handler handler = new Handler(); - setAfterSaveDatabase(new AfterRoundSave((AppCompatActivity) getActivity(), handler, rounds, oldRounds)); + setAfterSaveDatabaseRunnable(new AfterRoundSave((AppCompatActivity) getActivity(), rounds, oldRounds)); } super.onDialogClosed(positiveResult); } - private class AfterRoundSave extends OnFinishRunnable { + private class AfterRoundSave extends ActionRunnable { private long mNewRounds; private long mOldRounds; private AppCompatActivity mActivity; - AfterRoundSave(AppCompatActivity ctx, Handler handler, long newRounds, long oldRounds) { - super(handler); + AfterRoundSave(AppCompatActivity ctx, long newRounds, long oldRounds) { + super(); mActivity = ctx; mNewRounds = newRounds; @@ -98,22 +95,19 @@ public class RoundsPreferenceDialogFragmentCompat extends InputDatabaseSavePrefe } @Override - public void run() { + public void onFinishRun(boolean isSuccess, String message) { if (mActivity != null) { mActivity.runOnUiThread(() -> { long roundsToShow = mNewRounds; - if (!mSuccess) { + if (!isSuccess) { displayMessage(mActivity); - database.setNumberKeyEncryptionRounds(mOldRounds); + getDatabase().setNumberKeyEncryptionRounds(mOldRounds); } getPreference().setSummary(String.valueOf(roundsToShow)); - SaveDatabaseProgressTaskDialogFragment.stop(mActivity); }); } - - super.run(); } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/adapter/ListRadioItemAdapter.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/adapter/ListRadioItemAdapter.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.java index b8b606cb1..2a96e558b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/adapter/ListRadioItemAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioItemAdapter.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment.adapter; +package com.kunzisoft.keepass.settings.preferencedialogfragment.adapter; import android.content.Context; import android.support.annotation.NonNull; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/adapter/ListRadioViewHolder.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioViewHolder.java similarity index 94% rename from app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/adapter/ListRadioViewHolder.java rename to app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioViewHolder.java index fa0f6b7df..dd2549c34 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferenceDialogFragment/adapter/ListRadioViewHolder.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/adapter/ListRadioViewHolder.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.settings.preferenceDialogFragment.adapter; +package com.kunzisoft.keepass.settings.preferencedialogfragment.adapter; import android.support.v7.widget.RecyclerView; import android.view.View; diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt new file mode 100644 index 000000000..55ee20094 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.tasks + +import android.content.Context +import android.widget.Toast + +/** + * Callback after a task is completed. + */ +abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable? = null, + private var executeNestedActionIfResultFalse: Boolean = false) : Runnable { + + protected var isSuccess: Boolean = true + protected var message: String? = null + + private fun setResult(result: Boolean, message: String? = null) { + this.isSuccess = result + this.message = message + } + + private fun execute() { + nestedActionRunnable?.let { + // Pass on result on call finish + it.setResult(isSuccess, message) + it.run() + } + onFinishRun(isSuccess, message) + } + + override fun run() { + execute() + } + + /** + * If [success] or [executeNestedActionIfResultFalse] true, + * launch the nested action runnable if exists and finish, + * else directly finish + */ + protected fun finishRun(success: Boolean, message: String? = null) { + setResult(success, message) + if (success || executeNestedActionIfResultFalse) { + execute() + } + else + onFinishRun(isSuccess, message) + } + + /** + * Method called when the action is finished + * @param isSuccess 'true' if success action, 'false' elsewhere + * @param message + */ + abstract fun onFinishRun(isSuccess: Boolean, message: String?) + + /** + * ONLY to use in UIThread, typically in an Activity, Fragment or a Service + * @param ctx Context to show the message + */ + protected fun displayMessage(ctx: Context) { + message?.let { + if (it.isNotEmpty()) { + Toast.makeText(ctx, message, Toast.LENGTH_LONG).show() + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.java deleted file mode 100644 index 11574d3bf..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.tasks; - -import android.annotation.SuppressLint; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.utils.Util; - -public class ProgressTaskDialogFragment extends DialogFragment implements ProgressTaskUpdater{ - - public static final String PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment"; - - private static final int UNDEFINED = -1; - - private @StringRes int title = UNDEFINED; - private @StringRes int message = UNDEFINED; - - private TextView titleView; - private TextView messageView; - private ProgressBar progressView; - - public static ProgressTaskDialogFragment start(FragmentManager fragmentManager, @StringRes int titleId) { - // Create an instance of the dialog fragment and show it - ProgressTaskDialogFragment dialog = new ProgressTaskDialogFragment(); - dialog.updateTitle(titleId); - dialog.show(fragmentManager, PROGRESS_TASK_DIALOG_TAG); - return dialog; - } - - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - assert getActivity() != null; - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - // Get the layout inflater - LayoutInflater inflater = getActivity().getLayoutInflater(); - - // Inflate and set the layout for the dialog - // Pass null as the parent view because its going in the dialog layout - @SuppressLint("InflateParams") - View root = inflater.inflate(R.layout.progress_dialog, null); - builder.setView(root); - - titleView = root.findViewById(R.id.progress_dialog_title); - messageView = root.findViewById(R.id.progress_dialog_message); - progressView = root.findViewById(R.id.progress_dialog_bar); - - updateTitle(title); - updateMessage(message); - - setCancelable(false); - Util.lockScreenOrientation(getActivity()); - - return builder.create(); - } - - @Override - public void onDismiss(DialogInterface dialog) { - Util.unlockScreenOrientation(getActivity()); - super.onDismiss(dialog); - } - - public static void stop(AppCompatActivity activity) { - Fragment fragmentTask = activity.getSupportFragmentManager().findFragmentByTag(PROGRESS_TASK_DIALOG_TAG); - if (fragmentTask != null) { - ProgressTaskDialogFragment loadingDatabaseDialog = (ProgressTaskDialogFragment) fragmentTask; - loadingDatabaseDialog.dismissAllowingStateLoss(); - Util.unlockScreenOrientation(activity); - } - } - - public void setTitle(@StringRes int titleId) { - this.title = titleId; - } - - private void updateView(TextView textView, @StringRes int resId) { - if (textView != null) { - if (resId == UNDEFINED) { - textView.setVisibility(View.GONE); - } else { - textView.setText(resId); - textView.setVisibility(View.VISIBLE); - } - } - } - - public void updateTitle(int resId) { - this.title = resId; - updateView(titleView, title); - } - - @Override - public void updateMessage(int resId) { - this.message = resId; - updateView(messageView, message); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt new file mode 100644 index 000000000..d33812089 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt @@ -0,0 +1,132 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.tasks + +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.DialogInterface +import android.os.Bundle +import android.support.annotation.StringRes +import android.support.v4.app.DialogFragment +import android.support.v4.app.FragmentActivity +import android.support.v4.app.FragmentManager +import android.support.v7.app.AlertDialog +import android.view.View +import android.widget.ProgressBar +import android.widget.TextView +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.utils.Util + +open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater { + + @StringRes + private var title = UNDEFINED + @StringRes + private var message = UNDEFINED + + private var titleView: TextView? = null + private var messageView: TextView? = null + private var progressView: ProgressBar? = null + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + + activity?.let { + val builder = AlertDialog.Builder(it) + // Get the layout inflater + val inflater = it.layoutInflater + + // Inflate and set the layout for the dialog + // Pass null as the parent view because its going in the dialog layout + @SuppressLint("InflateParams") + val root = inflater.inflate(R.layout.progress_dialog, null) + builder.setView(root) + + titleView = root.findViewById(R.id.progress_dialog_title) + messageView = root.findViewById(R.id.progress_dialog_message) + progressView = root.findViewById(R.id.progress_dialog_bar) + + updateTitle(title) + updateMessage(message) + + isCancelable = false + Util.lockScreenOrientation(it) + + return builder.create() + } + return super.onCreateDialog(savedInstanceState) + } + + override fun onDismiss(dialog: DialogInterface?) { + Util.unlockScreenOrientation(activity) + super.onDismiss(dialog) + } + + fun setTitle(@StringRes titleId: Int) { + this.title = titleId + } + + private fun updateView(textView: TextView?, @StringRes resId: Int) { + if (resId == UNDEFINED) { + textView?.visibility = View.GONE + } else { + textView?.setText(resId) + textView?.visibility = View.VISIBLE + } + } + + fun updateTitle(resId: Int) { + this.title = resId + updateView(titleView, title) + } + + override fun updateMessage(resId: Int) { + this.message = resId + updateView(messageView, message) + } + + companion object { + + private const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment" + + private const val UNDEFINED = -1 + + fun start(fragmentManager: FragmentManager, + @StringRes titleId: Int, + @StringRes messageId: Int? = null): ProgressTaskDialogFragment { + // Create an instance of the dialog fragment and show it + val dialog = ProgressTaskDialogFragment() + dialog.updateTitle(titleId) + messageId?.let { + dialog.updateMessage(it) + } + dialog.show(fragmentManager, PROGRESS_TASK_DIALOG_TAG) + return dialog + } + + fun stop(activity: FragmentActivity) { + val fragmentTask = activity.supportFragmentManager.findFragmentByTag(PROGRESS_TASK_DIALOG_TAG) + if (fragmentTask != null) { + val loadingDatabaseDialog = fragmentTask as ProgressTaskDialogFragment + loadingDatabaseDialog.dismissAllowingStateLoss() + Util.unlockScreenOrientation(activity) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/SaveDatabaseProgressTaskDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/tasks/SaveDatabaseProgressTaskDialogFragment.java deleted file mode 100644 index 105223b84..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/SaveDatabaseProgressTaskDialogFragment.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.kunzisoft.keepass.tasks; - -import android.support.v4.app.FragmentManager; - -import com.kunzisoft.keepass.R; - -public class SaveDatabaseProgressTaskDialogFragment extends ProgressTaskDialogFragment { - - public static SaveDatabaseProgressTaskDialogFragment start(FragmentManager fragmentManager) { - // Create an instance of the dialog fragment and show it - SaveDatabaseProgressTaskDialogFragment dialog = new SaveDatabaseProgressTaskDialogFragment(); - dialog.updateTitle(R.string.saving_database); - dialog.show(fragmentManager, PROGRESS_TASK_DIALOG_TAG); - return dialog; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/UIToastTask.java b/app/src/main/java/com/kunzisoft/keepass/tasks/UIToastTask.java deleted file mode 100644 index a891f10db..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/UIToastTask.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.tasks; - -import android.content.Context; -import android.widget.Toast; - -public class UIToastTask implements Runnable { - - private String mText; - private Context mCtx; - - public UIToastTask(Context ctx, int resId) { - mCtx = ctx; - mText = ctx.getString(resId); - } - - public UIToastTask(Context ctx, String text) { - mCtx = ctx; - mText = text; - } - - public void run() { - Toast.makeText(mCtx, mText, Toast.LENGTH_LONG).show(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/UpdateProgressTaskStatus.java b/app/src/main/java/com/kunzisoft/keepass/tasks/UpdateProgressTaskStatus.java deleted file mode 100644 index 2e2ed1eed..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/UpdateProgressTaskStatus.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.tasks; - -import android.content.Context; -import android.os.Handler; - -public class UpdateProgressTaskStatus implements ProgressTaskUpdater { - private Context mContext; - private ProgressTaskUpdater mProgressTaskUpdater; - private Handler mHandler; - - public UpdateProgressTaskStatus(Context context, ProgressTaskUpdater progressTaskUpdater) { - this(context, new Handler(), progressTaskUpdater); - } - - public UpdateProgressTaskStatus(Context context, Handler handler, ProgressTaskUpdater progressTaskUpdater) { - this.mContext = context; - this.mProgressTaskUpdater = progressTaskUpdater; - this.mHandler = handler; - } - - @Override - public void updateMessage(int resId) { - if ( mContext != null && mProgressTaskUpdater != null && mHandler != null ) { - mHandler.post(new UpdateMessage(resId)); - } - } - - private class UpdateMessage implements Runnable { - private int mResId; - - UpdateMessage(int resId) { - mResId = resId; - } - - public void run() { - mProgressTaskUpdater.updateMessage(mResId); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.java b/app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.java deleted file mode 100644 index 600bec421..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * - * Copyright 2018 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.timeout; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.text.SpannableString; -import android.text.method.LinkMovementMethod; -import android.text.util.Linkify; -import android.widget.TextView; -import android.widget.Toast; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.exception.SamsungClipboardException; -import com.kunzisoft.keepass.tasks.UIToastTask; - -import java.util.Timer; -import java.util.TimerTask; - -public class ClipboardHelper { - - private Context context; - private ClipboardManager clipboardManager; - - private Timer mTimer = new Timer(); - - public ClipboardHelper(Context context) { - this.context = context; - this.clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - } - - public void timeoutCopyToClipboard(String text) { - timeoutCopyToClipboard(text, ""); - } - - public void timeoutCopyToClipboard(String text, String toastString) { - if (!toastString.isEmpty()) - Toast.makeText(context, toastString, Toast.LENGTH_LONG).show(); - try { - copyToClipboard(text); - } catch (SamsungClipboardException e) { - showSamsungDialog(); - return; - } - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String sClipClear = prefs.getString(context.getString(R.string.clipboard_timeout_key), - context.getString(R.string.clipboard_timeout_default)); - - long clipClearTime = Long.parseLong(sClipClear); - - if ( clipClearTime > 0 ) { - mTimer.schedule(new ClearClipboardTask(context, text), clipClearTime); - } - } - - public CharSequence getClipboard(Context context) { - if (clipboardManager.hasPrimaryClip()) { - ClipData data = clipboardManager.getPrimaryClip(); - if (data.getItemCount() > 0) { - CharSequence text = data.getItemAt(0).coerceToText(context); - if (text != null) { - return text; - } - } - } - return ""; - } - - public void copyToClipboard(String value) throws SamsungClipboardException { - copyToClipboard("", value); - } - - public void copyToClipboard(String label, String value) throws SamsungClipboardException { - try { - clipboardManager.setPrimaryClip(ClipData.newPlainText(label, value)); - } catch (Exception e) { - throw new SamsungClipboardException(e); - } - } - - public void cleanClipboard() throws SamsungClipboardException { - cleanClipboard(""); - } - - public void cleanClipboard(String label) throws SamsungClipboardException { - copyToClipboard(label,""); - } - - - // Setup to allow the toast to happen in the foreground - private final Handler uiThreadCallback = new Handler(); - - // Task which clears the clipboard, and sends a toast to the foreground. - private class ClearClipboardTask extends TimerTask { - - private final String mClearText; - private final Context mCtx; - - ClearClipboardTask(Context ctx, String clearText) { - mClearText = clearText; - mCtx = ctx; - } - - @Override - public void run() { - String currentClip = getClipboard(mCtx).toString(); - if ( currentClip.equals(mClearText) ) { - try { - cleanClipboard(); - uiThreadCallback.post(new UIToastTask(mCtx, R.string.clipboard_cleared)); - } catch (SamsungClipboardException e) { - uiThreadCallback.post(new UIToastTask(mCtx, R.string.clipboard_error_clear)); - } - } - } - } - - private void showSamsungDialog() { - String text = context.getString(R.string.clipboard_error).concat(System.getProperty("line.separator")) - .concat(context.getString(R.string.clipboard_error_url)); - SpannableString s = new SpannableString(text); - TextView tv = new TextView(context); - tv.setText(s); - tv.setAutoLinkMask(Activity.RESULT_OK); - tv.setMovementMethod(LinkMovementMethod.getInstance()); - Linkify.addLinks(s, Linkify.WEB_URLS); - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.clipboard_error_title) - .setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()) - .setView(tv) - .show(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.kt new file mode 100644 index 000000000..4565f7dba --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/ClipboardHelper.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.timeout + +import android.app.Activity +import android.app.AlertDialog +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Handler +import android.preference.PreferenceManager +import android.text.SpannableString +import android.text.method.LinkMovementMethod +import android.text.util.Linkify +import android.widget.TextView +import android.widget.Toast +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.exception.SamsungClipboardException +import java.util.* + +class ClipboardHelper(private val context: Context) { + + private val clipboardManager: ClipboardManager = + context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + private val mTimer = Timer() + + // Setup to allow the toast to happen in the foreground + private val uiThreadCallback = Handler() + + @JvmOverloads + fun timeoutCopyToClipboard(text: String, toastString: String = "") { + if (!toastString.isEmpty()) + Toast.makeText(context, toastString, Toast.LENGTH_LONG).show() + try { + copyToClipboard(text) + } catch (e: SamsungClipboardException) { + showSamsungDialog() + return + } + + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val sClipClear = prefs.getString(context.getString(R.string.clipboard_timeout_key), + context.getString(R.string.clipboard_timeout_default)) + + val clipClearTime = java.lang.Long.parseLong(sClipClear) + + if (clipClearTime > 0) { + mTimer.schedule(ClearClipboardTask(context, text), clipClearTime) + } + } + + fun getClipboard(context: Context): CharSequence { + if (clipboardManager.hasPrimaryClip()) { + val data = clipboardManager.primaryClip + if (data!!.itemCount > 0) { + val text = data.getItemAt(0).coerceToText(context) + if (text != null) { + return text + } + } + } + return "" + } + + @Throws(SamsungClipboardException::class) + fun copyToClipboard(value: String) { + copyToClipboard("", value) + } + + @Throws(SamsungClipboardException::class) + fun copyToClipboard(label: String, value: String) { + try { + clipboardManager.primaryClip = ClipData.newPlainText(label, value) + } catch (e: Exception) { + throw SamsungClipboardException(e) + } + + } + + @Throws(SamsungClipboardException::class) + @JvmOverloads + fun cleanClipboard(label: String = "") { + copyToClipboard(label, "") + } + + // Task which clears the clipboard, and sends a toast to the foreground. + private inner class ClearClipboardTask (private val mCtx: Context, + private val mClearText: String) : TimerTask() { + + override fun run() { + val currentClip = getClipboard(mCtx).toString() + if (currentClip == mClearText) { + try { + cleanClipboard() + uiThreadCallback.post { + Toast.makeText(mCtx, + R.string.clipboard_cleared, + Toast.LENGTH_LONG).show() + } + } catch (e: SamsungClipboardException) { + uiThreadCallback.post { + Toast.makeText(mCtx, + R.string.clipboard_error_clear, + Toast.LENGTH_LONG).show() + } + } + } + } + } + + private fun showSamsungDialog() { + val text = context.getString(R.string.clipboard_error)+ + System.getProperty("line.separator") + + context.getString(R.string.clipboard_error_url) + val s = SpannableString(text) + val tv = TextView(context) + tv.text = s + tv.autoLinkMask = Activity.RESULT_OK + tv.movementMethod = LinkMovementMethod.getInstance() + Linkify.addLinks(s, Linkify.WEB_URLS) + val builder = AlertDialog.Builder(context) + builder.setTitle(R.string.clipboard_error_title) + .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() } + .setView(tv) + .show() + } +} From 1bfdae53c65074525e4837e723d82b24b8b25298 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 14 Mar 2019 18:16:49 +0100 Subject: [PATCH 082/289] Add a warning message in save progress dialog --- .../SaveDatabaseProgressDialogRunnable.kt | 6 ++++-- .../tasks/ProgressTaskDialogFragment.kt | 20 ++++++++++++++++--- .../keepass/tasks/ProgressTaskUpdater.java | 4 +++- app/src/main/res/layout/progress_dialog.xml | 11 ++++++++++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt index c458aa06a..72f8c1a0d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt @@ -26,7 +26,7 @@ import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment import com.kunzisoft.keepass.tasks.ProgressTaskUpdater -open class SaveDatabaseProgressDialogRunnable(var contextDatabase: FragmentActivity, +open class SaveDatabaseProgressDialogRunnable(private var contextDatabase: FragmentActivity, database: Database, private val actionRunnable: ((ProgressTaskUpdater)-> ActionRunnable)?, save: Boolean): @@ -36,7 +36,9 @@ open class SaveDatabaseProgressDialogRunnable(var contextDatabase: FragmentActiv // Show the dialog val progressTaskUpdater : ProgressTaskUpdater = ProgressTaskDialogFragment.start( contextDatabase.supportFragmentManager, - R.string.saving_database) + R.string.saving_database, + null, + R.string.do_not_kill_app) // Do the action if defined actionRunnable?.invoke(progressTaskUpdater)?.run() diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt index d33812089..647f39622 100644 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt @@ -40,9 +40,12 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater { private var title = UNDEFINED @StringRes private var message = UNDEFINED + @StringRes + private var warning = UNDEFINED private var titleView: TextView? = null private var messageView: TextView? = null + private var warningView: TextView? = null private var progressView: ProgressBar? = null override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { @@ -60,10 +63,12 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater { titleView = root.findViewById(R.id.progress_dialog_title) messageView = root.findViewById(R.id.progress_dialog_message) + warningView = root.findViewById(R.id.progress_dialog_warning) progressView = root.findViewById(R.id.progress_dialog_bar) updateTitle(title) updateMessage(message) + updateWarning(warning) isCancelable = false Util.lockScreenOrientation(it) @@ -91,16 +96,21 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater { } } - fun updateTitle(resId: Int) { + fun updateTitle(@StringRes resId: Int) { this.title = resId updateView(titleView, title) } - override fun updateMessage(resId: Int) { + override fun updateMessage(@StringRes resId: Int) { this.message = resId updateView(messageView, message) } + fun updateWarning(@StringRes resId: Int) { + this.warning = resId + updateView(warningView, warning) + } + companion object { private const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment" @@ -109,13 +119,17 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater { fun start(fragmentManager: FragmentManager, @StringRes titleId: Int, - @StringRes messageId: Int? = null): ProgressTaskDialogFragment { + @StringRes messageId: Int? = null, + @StringRes warningId: Int? = null): ProgressTaskDialogFragment { // Create an instance of the dialog fragment and show it val dialog = ProgressTaskDialogFragment() dialog.updateTitle(titleId) messageId?.let { dialog.updateMessage(it) } + warningId?.let { + dialog.updateWarning(it) + } dialog.show(fragmentManager, PROGRESS_TASK_DIALOG_TAG) return dialog } diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.java b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.java index 23188c78b..a41d0c7cc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.java +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskUpdater.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.tasks; +import android.support.annotation.StringRes; + public interface ProgressTaskUpdater { - void updateMessage(int resId); + void updateMessage(@StringRes int resId); } diff --git a/app/src/main/res/layout/progress_dialog.xml b/app/src/main/res/layout/progress_dialog.xml index 4eae38997..2898d42e4 100644 --- a/app/src/main/res/layout/progress_dialog.xml +++ b/app/src/main/res/layout/progress_dialog.xml @@ -26,6 +26,17 @@ android:layout_marginEnd="20dp" style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/> + + Parallelism Degree of parallelism (i.e. number of threads) used by the key derivation function. Saving database… + Do not kill the app… Space Search Sort From dad3edee5ea2a1f30c4778750a671ef05a58fbfa Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 15 Mar 2019 10:58:04 +0100 Subject: [PATCH 083/289] Refactor database elements --- .../kunzisoft/keepass/tests/PwDateTest.java | 2 +- .../keepass/tests/PwEntryTestV3.java | 2 +- .../keepass/tests/PwEntryTestV4.java | 8 ++-- .../kunzisoft/keepass/tests/PwGroupTest.java | 2 +- .../kunzisoft/keepass/tests/TypesTest.java | 2 +- .../keepass/tests/database/DeleteEntry.java | 12 ++--- .../keepass/tests/database/EntryV4.java | 4 +- .../keepass/tests/database/Kdb3Twofish.java | 4 +- .../keepass/tests/database/Kdb4.java | 48 +++++++++---------- .../keepass/tests/database/Kdb4Header.java | 9 ++-- .../keepass/tests/database/SprEngineTest.java | 21 ++++---- .../keepass/tests/database/TestData.java | 4 +- .../tests/output/PwManagerOutputTest.java | 6 +-- .../keepass/tests/search/SearchTest.java | 4 +- .../keepass/activities/EntryActivity.java | 6 +-- .../keepass/activities/EntryEditActivity.java | 14 +++--- .../keepass/activities/GroupActivity.java | 18 +++---- .../keepass/activities/ListNodesFragment.java | 6 +-- .../keepass/adapters/NodeAdapter.java | 9 ++-- .../adapters/SearchEntryCursorAdapter.java | 8 ++-- .../java/com/kunzisoft/keepass/app/App.java | 2 +- .../keepass/autofill/AutofillHelper.kt | 2 +- .../keepass/crypto/engine/AesEngine.java | 2 +- .../keepass/crypto/engine/ChaCha20Engine.java | 2 +- .../keepass/crypto/engine/CipherEngine.java | 2 +- .../keepass/crypto/engine/TwofishEngine.java | 2 +- .../keepass/database/BinaryPool.java | 2 + .../keepass/database/EntryHandler.java | 2 + .../keepass/database/ExtraFields.java | 10 ++-- .../keepass/database/ISmallTimeLogger.java | 2 + .../keepass/database/ITimeLogger.java | 2 + .../database/MemoryProtectionConfig.java | 2 + .../keepass/database/SortNodeEnum.java | 4 ++ .../AssignPasswordInDatabaseRunnable.kt | 2 +- .../database/action/CreateDatabaseRunnable.kt | 4 +- .../database/action/LoadDatabaseRunnable.kt | 2 +- .../SaveDatabaseProgressDialogRunnable.kt | 2 +- .../database/action/SaveDatabaseRunnable.kt | 2 +- .../action/node/ActionNodeDatabaseRunnable.kt | 2 +- .../database/action/node/AddEntryRunnable.kt | 4 +- .../database/action/node/AddGroupRunnable.kt | 4 +- .../node/AfterActionNodeFinishRunnable.kt | 2 +- .../database/action/node/CopyEntryRunnable.kt | 6 +-- .../action/node/DeleteEntryRunnable.kt | 6 +-- .../action/node/DeleteGroupRunnable.java | 6 +-- .../database/action/node/MoveEntryRunnable.kt | 6 +-- .../database/action/node/MoveGroupRunnable.kt | 4 +- .../action/node/UpdateEntryRunnable.kt | 4 +- .../action/node/UpdateGroupRunnable.kt | 4 +- .../keepass/database/cursor/EntryCursor.java | 14 +++--- .../database/cursor/ExtraFieldCursor.java | 2 +- .../database/{ => element}/Database.java | 2 +- .../database/{ => element}/PwDatabase.java | 2 +- .../database/{ => element}/PwDatabaseV3.java | 2 +- .../{ => element}/PwDatabaseV3Debug.java | 2 +- .../database/{ => element}/PwDatabaseV4.java | 5 +- .../{ => element}/PwDatabaseV4XML.java | 2 +- .../database/{ => element}/PwDate.java | 2 +- .../database/{ => element}/PwDbHeader.java | 2 +- .../{ => element}/PwDbHeaderFactory.java | 2 +- .../database/{ => element}/PwDbHeaderV3.java | 2 +- .../database/{ => element}/PwDbHeaderV4.java | 6 ++- .../database/{ => element}/PwDefsV4.java | 2 +- .../{ => element}/PwDeletedObject.java | 2 +- .../{ => element}/PwEncryptionAlgorithm.java | 3 +- .../database/{ => element}/PwEntry.java | 3 +- .../database/{ => element}/PwEntryV3.java | 2 +- .../database/{ => element}/PwEntryV4.java | 5 +- .../database/{ => element}/PwGroup.java | 7 ++- .../database/{ => element}/PwGroupId.java | 2 +- .../database/{ => element}/PwGroupIdV3.java | 2 +- .../database/{ => element}/PwGroupIdV4.java | 2 +- .../database/{ => element}/PwGroupV3.java | 2 +- .../database/{ => element}/PwGroupV4.java | 4 +- .../database/{ => element}/PwIcon.java | 2 +- .../database/{ => element}/PwIconCustom.java | 2 +- .../database/{ => element}/PwIconFactory.java | 2 +- .../{ => element}/PwIconStandard.java | 2 +- .../database/{ => element}/PwNode.java | 3 +- .../database/{ => element}/PwVersion.java | 2 +- .../iterator/EntrySearchStringIterator.java | 6 +-- .../iterator/EntrySearchStringIteratorV3.java | 2 +- .../iterator/EntrySearchStringIteratorV4.java | 2 +- .../keepass/database/load/Importer.java | 2 +- .../database/load/ImporterFactory.java | 4 +- .../keepass/database/load/ImporterV3.java | 14 +++--- .../database/load/ImporterV3Debug.java | 2 +- .../keepass/database/load/ImporterV4.java | 18 +++---- .../database/save/PwDbHeaderOutputV3.java | 2 +- .../database/save/PwDbHeaderOutputV4.java | 6 +-- .../save/PwDbInnerHeaderOutputV4.java | 4 +- .../keepass/database/save/PwDbOutput.java | 8 ++-- .../keepass/database/save/PwDbV3Output.java | 12 ++--- .../database/save/PwDbV3OutputDebug.java | 6 +-- .../keepass/database/save/PwDbV4Output.java | 16 +++---- .../database/save/PwEntryOutputV3.java | 2 +- .../database/save/PwGroupOutputV3.java | 2 +- .../database/search/EntrySearchHandler.java | 2 +- .../search/EntrySearchHandlerAll.java | 2 +- .../database/search/EntrySearchHandlerV4.java | 6 +-- .../database/search/EntrySearchV4.java | 4 +- .../database/search/SearchDbHelper.java | 18 +++---- .../dialogs/GroupEditDialogFragment.java | 6 +-- .../dialogs/IconPickerDialogFragment.java | 2 +- .../keepass/icons/IconDrawableFactory.java | 6 +-- .../keepass/password/PasswordActivity.java | 2 +- .../settings/MainPreferenceFragment.java | 2 +- .../settings/NestedSettingsFragment.java | 2 +- ...gorithmPreferenceDialogFragmentCompat.java | 2 +- ...abaseSavePreferenceDialogFragmentCompat.kt | 2 +- .../kunzisoft/keepass/utils/EmptyUtils.java | 2 +- .../kunzisoft/keepass/utils/SprContextV4.java | 4 +- .../kunzisoft/keepass/utils/SprEngineV4.java | 8 ++-- 113 files changed, 294 insertions(+), 263 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/Database.java (99%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDatabase.java (99%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDatabaseV3.java (99%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDatabaseV3Debug.java (96%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDatabaseV4.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDatabaseV4XML.java (99%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDate.java (99%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDbHeader.java (95%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDbHeaderFactory.java (95%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDbHeaderV3.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDbHeaderV4.java (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDefsV4.java (95%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwDeletedObject.java (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwEncryptionAlgorithm.java (95%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwEntry.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwEntryV3.java (99%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwEntryV4.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwGroup.java (95%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwGroupId.java (95%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwGroupIdV3.java (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwGroupIdV4.java (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwGroupV3.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwGroupV4.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwIcon.java (95%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwIconCustom.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwIconFactory.java (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwIconStandard.java (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwNode.java (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => element}/PwVersion.java (95%) diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java index c398b062b..069e8af7e 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.tests; import junit.framework.TestCase; -import com.kunzisoft.keepass.database.PwDate; +import com.kunzisoft.keepass.database.element.PwDate; public class PwDateTest extends TestCase { public void testDate() { diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java index f5b8e2b14..7cea7bc36 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java @@ -27,7 +27,7 @@ import java.util.Calendar; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.tests.database.TestData; public class PwEntryTestV3 extends AndroidTestCase { diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java index bae5a731a..a6237abf6 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java @@ -20,10 +20,10 @@ package com.kunzisoft.keepass.tests; import com.kunzisoft.keepass.database.AutoType; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwGroupV4; -import com.kunzisoft.keepass.database.PwIconCustom; -import com.kunzisoft.keepass.database.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwIconCustom; +import com.kunzisoft.keepass.database.element.PwIconStandard; import com.kunzisoft.keepass.database.security.ProtectedBinary; import com.kunzisoft.keepass.database.security.ProtectedString; diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java index 3473d0ac2..db98a6371 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.tests; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.PwGroupV3; +import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.tests.database.TestData; public class PwGroupTest extends AndroidTestCase { diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java index 38b1a0fa1..d78d39932 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java @@ -28,7 +28,7 @@ import java.util.UUID; import junit.framework.TestCase; -import com.kunzisoft.keepass.database.PwDate; +import com.kunzisoft.keepass.database.element.PwDate; import com.kunzisoft.keepass.stream.LEDataInputStream; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.utils.Types; diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index 88597ae20..0fa3fa0e9 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -22,12 +22,12 @@ package com.kunzisoft.keepass.tests.database; import android.content.Context; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwDatabaseV3; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwEntryV3; -import com.kunzisoft.keepass.database.PwGroup; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDatabaseV3; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwGroup; import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; import com.kunzisoft.keepass.database.search.SearchDbHelper; diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java index b20fa9b32..d15671cf7 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java @@ -19,8 +19,8 @@ */ package com.kunzisoft.keepass.tests.database; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwEntryV4; import junit.framework.TestCase; diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java index f04027487..d39f37a29 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java @@ -25,8 +25,8 @@ import android.content.Context; import android.content.res.AssetManager; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.PwDatabaseV3; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwDatabaseV3; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import com.kunzisoft.keepass.database.load.ImporterV3; public class Kdb3Twofish extends AndroidTestCase { diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4.java index e09591a1c..74e6323e2 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4.java @@ -19,27 +19,17 @@ */ package com.kunzisoft.keepass.tests.database; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - import android.content.Context; import android.content.res.AssetManager; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.PwDatabaseV4; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.PwDbOutputException; -import com.kunzisoft.keepass.database.load.Importer; -import com.kunzisoft.keepass.database.load.ImporterFactory; -import com.kunzisoft.keepass.database.load.ImporterV4; -import com.kunzisoft.keepass.database.save.PwDbOutput; -import com.kunzisoft.keepass.database.save.PwDbV4Output; -import com.kunzisoft.keepass.stream.CopyInputStream; import com.kunzisoft.keepass.tests.TestUtil; +import java.io.IOException; +import java.io.InputStream; + public class Kdb4 extends AndroidTestCase { public void testDetection() throws IOException, InvalidDBException { @@ -48,11 +38,13 @@ public class Kdb4 extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); + /* + TODO Test Importer importer = ImporterFactory.createImporter(is); assertTrue(importer instanceof ImporterV4); is.close(); - + */ } public void testParsing() throws IOException, InvalidDBException { @@ -61,12 +53,13 @@ public class Kdb4 extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); + /* + TODO Test ImporterV4 importer = new ImporterV4(); importer.openDatabase(is, "12345", null); is.close(); - - + */ } public void testSavingKDBXV3() throws IOException, InvalidDBException, PwDbOutputException { @@ -83,6 +76,8 @@ public class Kdb4 extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open(inputFile, AssetManager.ACCESS_STREAMING); + /* + TODO Test ImporterV4 importer = new ImporterV4(); PwDatabaseV4 db = importer.openDatabase(is, password, null); is.close(); @@ -103,7 +98,7 @@ public class Kdb4 extends AndroidTestCase { bis.close(); fos.close(); - + */ } @Override @@ -120,10 +115,13 @@ public class Kdb4 extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("keyfile.kdbx", AssetManager.ACCESS_STREAMING); + /* + TODO Test ImporterV4 importer = new ImporterV4(); importer.openDatabase(is, "12345", TestUtil.getKeyFileInputStream(ctx, TestUtil.getSdPath("key"))); is.close(); + */ } @@ -133,11 +131,13 @@ public class Kdb4 extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("keyfile-binary.kdbx", AssetManager.ACCESS_STREAMING); + /* + TODO Test ImporterV4 importer = new ImporterV4(); importer.openDatabase(is, "12345", TestUtil.getKeyFileInputStream(ctx,TestUtil.getSdPath("key-binary"))); is.close(); - + */ } public void testKeyfile() throws IOException, InvalidDBException { @@ -145,13 +145,13 @@ public class Kdb4 extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("key-only.kdbx", AssetManager.ACCESS_STREAMING); - + /* + TODO Test ImporterV4 importer = new ImporterV4(); importer.openDatabase(is, "", TestUtil.getKeyFileInputStream(ctx, TestUtil.getSdPath("key"))); is.close(); - - + */ } public void testNoGzip() throws IOException, InvalidDBException { @@ -159,13 +159,13 @@ public class Kdb4 extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("no-encrypt.kdbx", AssetManager.ACCESS_STREAMING); - + /* + TODO Test ImporterV4 importer = new ImporterV4(); importer.openDatabase(is, "12345", null); is.close(); - - + */ } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java index a29790227..6dc7c0dcf 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java @@ -23,10 +23,6 @@ import android.content.Context; import android.content.res.AssetManager; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.crypto.engine.AesEngine; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.load.ImporterV4; - import java.io.InputStream; public class Kdb4Header extends AndroidTestCase { @@ -35,7 +31,9 @@ public class Kdb4Header extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); - + + /* + TODO Test ImporterV4 importer = new ImporterV4(); PwDatabaseV4 db = importer.openDatabase(is, "12345", null); @@ -45,6 +43,7 @@ public class Kdb4Header extends AndroidTestCase { assertTrue(db.getDataCipher().equals(AesEngine.CIPHER_UUID)); is.close(); + */ } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java index 62a171608..d666c6895 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java @@ -19,21 +19,21 @@ */ package com.kunzisoft.keepass.tests.database; -import java.io.InputStream; -import java.util.UUID; - import android.content.Context; import android.content.res.AssetManager; import android.test.AndroidTestCase; -import biz.source_code.base64Coder.Base64Coder; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.load.ImporterV4; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.utils.SprEngineV4; import com.kunzisoft.keepass.utils.Types; +import java.io.InputStream; +import java.util.UUID; + +import biz.source_code.base64Coder.Base64Coder; + public class SprEngineTest extends AndroidTestCase { private PwDatabaseV4 db; private SprEngineV4 spr; @@ -46,13 +46,16 @@ public class SprEngineTest extends AndroidTestCase { AssetManager am = ctx.getAssets(); InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); - + + /* + TODO Test ImporterV4 importer = new ImporterV4(); db = importer.openDatabase(is, "12345", null); is.close(); spr = new SprEngineV4(); + */ } private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}"; diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java index dcf7ce5d4..0e6fa1e27 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java @@ -25,8 +25,8 @@ import android.content.Context; import android.content.res.AssetManager; import android.net.Uri; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwDatabaseV3Debug; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; import com.kunzisoft.keepass.database.load.Importer; import com.kunzisoft.keepass.tests.TestUtil; diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java index 46b414d25..6841ccd1c 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java @@ -32,9 +32,9 @@ import java.security.NoSuchAlgorithmException; import android.content.res.AssetManager; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.PwDatabaseV3Debug; -import com.kunzisoft.keepass.database.PwDbHeader; -import com.kunzisoft.keepass.database.PwDbHeaderV3; +import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; +import com.kunzisoft.keepass.database.element.PwDbHeader; +import com.kunzisoft.keepass.database.element.PwDbHeaderV3; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.database.save.PwDbHeaderOutputV3; import com.kunzisoft.keepass.database.save.PwDbV3Output; diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java index 949ba6ca2..d05fd585a 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java @@ -25,8 +25,8 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwGroup; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwGroup; import com.kunzisoft.keepass.tests.database.TestData; public class SearchTest extends AndroidTestCase { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 558728834..e095cd30c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -42,10 +42,10 @@ import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.ExtraFields; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwEntry; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwEntry; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.notifications.NotificationCopyingService; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index a5d44aee7..0b9e04119 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -42,13 +42,13 @@ import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwDate; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.database.PwGroupId; -import com.kunzisoft.keepass.database.PwIconStandard; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDate; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwGroupId; +import com.kunzisoft.keepass.database.element.PwIconStandard; import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.database.action.node.ActionNodeValues; import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index b61f07ac9..d22943575 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -59,15 +59,15 @@ import com.kunzisoft.keepass.adapters.NodeAdapter; import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.database.PwGroupId; -import com.kunzisoft.keepass.database.PwGroupV4; -import com.kunzisoft.keepass.database.PwIcon; -import com.kunzisoft.keepass.database.PwIconStandard; -import com.kunzisoft.keepass.database.PwNode; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwGroupId; +import com.kunzisoft.keepass.database.element.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwIcon; +import com.kunzisoft.keepass.database.element.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java index c7d8db722..32bee8ea3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java @@ -20,9 +20,9 @@ import android.view.ViewGroup; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.adapters.NodeAdapter; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.database.PwNode; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; import com.kunzisoft.keepass.dialogs.SortDialogFragment; import com.kunzisoft.keepass.settings.PreferencesUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 752b23598..9bdd9ab87 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -27,7 +27,6 @@ import android.support.v7.util.SortedList; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.util.SortedListAdapterCallback; import android.util.Log; -import android.util.TypedValue; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MenuInflater; @@ -38,10 +37,10 @@ import android.widget.Toast; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.database.PwNode; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.utils.Util; diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java index 1914d857f..363553a34 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java @@ -31,10 +31,10 @@ import android.widget.ImageView; import android.widget.TextView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwIcon; -import com.kunzisoft.keepass.database.PwIconFactory; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwIcon; +import com.kunzisoft.keepass.database.element.PwIconFactory; import com.kunzisoft.keepass.database.cursor.EntryCursor; import com.kunzisoft.keepass.settings.PreferencesUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.java b/app/src/main/java/com/kunzisoft/keepass/app/App.java index 608766c02..2ccb90e21 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.java +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.java @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.app; import android.support.multidex.MultiDexApplication; import com.kunzisoft.keepass.compat.PRNGFixes; -import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.fileselect.RecentFileHistory; import com.kunzisoft.keepass.stylish.Stylish; diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt index 89b119c27..e04dffa17 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -33,7 +33,7 @@ import android.view.autofill.AutofillValue import android.widget.RemoteViews import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.EntrySelectionHelper -import com.kunzisoft.keepass.database.PwEntry +import com.kunzisoft.keepass.database.element.PwEntry import java.util.* diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java index cccbf51cf..4ddc66664 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.crypto.engine; import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import com.kunzisoft.keepass.utils.Types; import java.security.InvalidAlgorithmParameterException; diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.java b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.java index 27d6ae852..64f9e1dc3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/ChaCha20Engine.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.crypto.engine; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import com.kunzisoft.keepass.utils.Types; import org.spongycastle.jce.provider.BouncyCastleProvider; diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/CipherEngine.java b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/CipherEngine.java index 713e2e81d..3e6c2f989 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/CipherEngine.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/CipherEngine.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.crypto.engine; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.java b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.java index 4cb9736b7..d0189385d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/TwofishEngine.java @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.crypto.engine; import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import com.kunzisoft.keepass.utils.Types; import java.security.InvalidAlgorithmParameterException; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java index c256d5fd9..ddbe51f13 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.security.ProtectedBinary; import java.util.Collection; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java index 5db793118..f8adf1f75 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database; +import com.kunzisoft.keepass.database.element.PwEntry; + /** "Delegate" class for operating on each entry when traversing all of * them * @author bpellin diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java index 77e637e25..53ef5820b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java @@ -29,11 +29,11 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import static com.kunzisoft.keepass.database.PwEntryV4.STR_NOTES; -import static com.kunzisoft.keepass.database.PwEntryV4.STR_PASSWORD; -import static com.kunzisoft.keepass.database.PwEntryV4.STR_TITLE; -import static com.kunzisoft.keepass.database.PwEntryV4.STR_URL; -import static com.kunzisoft.keepass.database.PwEntryV4.STR_USERNAME; +import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_NOTES; +import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_PASSWORD; +import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_TITLE; +import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_URL; +import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_USERNAME; public class ExtraFields implements Parcelable, Cloneable { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ISmallTimeLogger.java b/app/src/main/java/com/kunzisoft/keepass/database/ISmallTimeLogger.java index c7c8c84ec..b01d3c890 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ISmallTimeLogger.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ISmallTimeLogger.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database; +import com.kunzisoft.keepass.database.element.PwDate; + public interface ISmallTimeLogger { PwDate getLastModificationTime(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java b/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java index 09326a20f..35117cbe7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database; +import com.kunzisoft.keepass.database.element.PwDate; + public interface ITimeLogger extends ISmallTimeLogger { long getUsageCount(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java b/app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java index bc3f05aa5..1f31f5276 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database; +import com.kunzisoft.keepass.database.element.PwDefsV4; + public class MemoryProtectionConfig { public boolean protectTitle = false; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java index b921c24a1..923139dd1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java @@ -20,6 +20,10 @@ package com.kunzisoft.keepass.database; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwNode; + import java.util.Comparator; public enum SortNodeEnum { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt index e5329e8ec..75b3ecc50 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.action import android.content.Context import android.net.Uri -import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.InvalidKeyFileException import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.UriUtil diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt index 4dd0b9a16..a3824ca61 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt @@ -20,8 +20,8 @@ package com.kunzisoft.keepass.database.action import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwDatabase +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwDatabase import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.UriUtil diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt index c4ba67037..71c068923 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt @@ -26,7 +26,7 @@ import android.support.annotation.StringRes import android.util.Log import com.kunzisoft.keepass.R import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.* import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskUpdater diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt index 72f8c1a0d..a547d95ea 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseProgressDialogRunnable.kt @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.action import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment import com.kunzisoft.keepass.tasks.ProgressTaskUpdater diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt index 38cb7d02a..ba767ac0b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.action import android.content.Context -import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.PwDbOutputException import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.timeout.TimeoutHelper diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt index bd548e721..b9ec443eb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt @@ -1,7 +1,7 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity -import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.action.SaveDatabaseProgressDialogRunnable abstract class ActionNodeDatabaseRunnable( diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt index 659f10007..74cc8a6b9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt @@ -20,8 +20,8 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwEntry +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwEntry class AddEntryRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt index 0180fc517..6e860ae71 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt @@ -20,8 +20,8 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwGroup +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwGroup class AddGroupRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt index a61ca1a88..886b50f8a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.action.node -import com.kunzisoft.keepass.database.PwNode +import com.kunzisoft.keepass.database.element.PwNode /** * Callback method who return the node(s) modified after an action diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt index 77da0fb0b..6b8b1c46e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt @@ -21,9 +21,9 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import android.util.Log -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwEntry -import com.kunzisoft.keepass.database.PwGroup +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwEntry +import com.kunzisoft.keepass.database.element.PwGroup class CopyEntryRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt index 3baff7ad7..401ab21fd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt @@ -20,9 +20,9 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwEntry -import com.kunzisoft.keepass.database.PwGroup +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwEntry +import com.kunzisoft.keepass.database.element.PwGroup class DeleteEntryRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java index 409f90faa..89113bfb3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java @@ -21,9 +21,9 @@ package com.kunzisoft.keepass.database.action.node; import android.support.v4.app.FragmentActivity; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwGroup; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwGroup; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt index cf19d29fc..c04f4abd1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt @@ -21,9 +21,9 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import android.util.Log -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwEntry -import com.kunzisoft.keepass.database.PwGroup +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwEntry +import com.kunzisoft.keepass.database.element.PwGroup class MoveEntryRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt index ecb896a86..cbf4bd0fa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt @@ -22,8 +22,8 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import android.util.Log import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwGroup +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwGroup class MoveGroupRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt index 24b5e3878..5dad29f64 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt @@ -20,8 +20,8 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwEntry +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwEntry class UpdateEntryRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt index 94e011c80..a68fc438f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt @@ -20,8 +20,8 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity -import com.kunzisoft.keepass.database.Database -import com.kunzisoft.keepass.database.PwGroup +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.PwGroup class UpdateGroupRunnable constructor( context: FragmentActivity, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java index 041d1e789..2adcc7e77 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java @@ -3,13 +3,13 @@ package com.kunzisoft.keepass.database.cursor; import android.database.MatrixCursor; import android.provider.BaseColumns; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwEntryV3; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwIconCustom; -import com.kunzisoft.keepass.database.PwIconFactory; -import com.kunzisoft.keepass.database.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwIconCustom; +import com.kunzisoft.keepass.database.element.PwIconFactory; +import com.kunzisoft.keepass.database.element.PwIconStandard; import java.util.UUID; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java index af618ebb7..c5159968a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java @@ -3,7 +3,7 @@ package com.kunzisoft.keepass.database.cursor; import android.database.MatrixCursor; import android.provider.BaseColumns; -import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.security.ProtectedString; public class ExtraFieldCursor extends MatrixCursor { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java similarity index 99% rename from app/src/main/java/com/kunzisoft/keepass/database/Database.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 6addc45c3..9f043b720 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.content.Context; import android.content.res.Resources; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java similarity index 99% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDatabase.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index 09b324178..b576173d0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.KeyFileEmptyException; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java similarity index 99% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index 8be18ff21..4c950c0df 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -43,7 +43,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import com.kunzisoft.keepass.crypto.finalkey.FinalKey; import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3Debug.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3Debug.java similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3Debug.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3Debug.java index 528094435..f9fb0bdf0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV3Debug.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3Debug.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; public class PwDatabaseV3Debug extends PwDatabaseV3 { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index bf7490bf6..ad72e058f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.util.Log; import android.webkit.URLUtil; @@ -29,6 +29,9 @@ import com.kunzisoft.keepass.crypto.engine.CipherEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; +import com.kunzisoft.keepass.database.BinaryPool; +import com.kunzisoft.keepass.database.MemoryProtectionConfig; +import com.kunzisoft.keepass.database.PwCompressionAlgorithm; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.UnknownKDF; import com.kunzisoft.keepass.utils.EmptyUtils; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV4XML.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4XML.java similarity index 99% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV4XML.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4XML.java index 51b1e9f89..f90b86a87 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDatabaseV4XML.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4XML.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import java.text.SimpleDateFormat; import java.util.TimeZone; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDate.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java similarity index 99% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDate.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java index 5326609a2..8b023c869 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDate.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeader.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDbHeader.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.java index 3681d65f8..cd0c86e03 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeader.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; public abstract class PwDbHeader { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderFactory.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderFactory.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderFactory.java index 22a99f99b..4d8a07e13 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderFactory.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; public class PwDbHeaderFactory { public static PwDbHeader getInstance(PwDatabase db) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderV3.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java index 4befe3671..6bdfdf1d8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java @@ -44,7 +44,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import com.kunzisoft.keepass.stream.LEDataInputStream; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java index ae31bcfa9..54096f664 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDbHeaderV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java @@ -17,11 +17,15 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; +import com.kunzisoft.keepass.database.CrsAlgorithm; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; +import com.kunzisoft.keepass.database.PwCompressionAlgorithm; import com.kunzisoft.keepass.database.exception.InvalidDBVersionException; import com.kunzisoft.keepass.stream.CopyInputStream; import com.kunzisoft.keepass.stream.HmacBlockStream; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDefsV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDefsV4.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDefsV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDefsV4.java index eaaf67da4..0ac29a07f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDefsV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDefsV4.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; public class PwDefsV4 { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwDeletedObject.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/PwDeletedObject.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java index 276122b56..8a85d3c5f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwDeletedObject.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import java.util.Date; import java.util.UUID; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEncryptionAlgorithm.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwEncryptionAlgorithm.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.java index 32c6a8dba..8974ce6fc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEncryptionAlgorithm.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.content.res.Resources; @@ -26,6 +26,7 @@ import com.kunzisoft.keepass.crypto.engine.AesEngine; import com.kunzisoft.keepass.crypto.engine.ChaCha20Engine; import com.kunzisoft.keepass.crypto.engine.CipherEngine; import com.kunzisoft.keepass.crypto.engine.TwofishEngine; +import com.kunzisoft.keepass.database.ObjectNameResource; import java.util.UUID; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.java index 8f72c888c..6c3084669 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntry.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.java @@ -17,10 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import com.kunzisoft.keepass.database.ExtraFields; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.model.Entry; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java similarity index 99% rename from app/src/main/java/com/kunzisoft/keepass/database/PwEntryV3.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index 6a93c7586..9c4c7ba88 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -40,7 +40,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 677db7f26..06eebc71a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -17,10 +17,13 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import com.kunzisoft.keepass.database.AutoType; +import com.kunzisoft.keepass.database.ExtraFields; +import com.kunzisoft.keepass.database.ITimeLogger; import com.kunzisoft.keepass.database.security.ProtectedBinary; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.utils.MemUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwGroup.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java index 6f97bd489..4f28a62a3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroup.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java @@ -17,10 +17,13 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; + import java.util.ArrayList; import java.util.List; @@ -155,7 +158,7 @@ public abstract class PwGroup } public boolean preOrderTraverseTree(GroupHandler groupHandler, - EntryHandler entryHandler) { + EntryHandler entryHandler) { if (entryHandler != null) { for (EntryE entry : childEntries) { if (!entryHandler.operate(entry)) return false; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupId.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupId.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwGroupId.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupId.java index b5c261673..31c7949e6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupId.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupId.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupIdV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV3.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/PwGroupIdV3.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV3.java index 24cb9126f..a07e645ac 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupIdV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV3.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupIdV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV4.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/PwGroupIdV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV4.java index 0617e5dd8..bdf136b76 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupIdV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV4.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwGroupV3.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 2da96b601..c1c3af69c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -18,7 +18,7 @@ * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index e1055c3ce..09881be69 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -17,11 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; -import com.kunzisoft.keepass.utils.MemUtil; +import com.kunzisoft.keepass.database.ITimeLogger; import java.util.HashMap; import java.util.Map; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIcon.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwIcon.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java index db23e3425..d317f4504 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIcon.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIconCustom.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwIconCustom.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java index a92d9cdad..850c75435 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIconCustom.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIconFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/PwIconFactory.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.java index 843e1bb71..79ea5011e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIconFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import org.apache.commons.collections.map.AbstractReferenceMap; import org.apache.commons.collections.map.ReferenceMap; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwIconStandard.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/PwIconStandard.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java index f0c73e0f9..f5f4b015d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwIconStandard.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/PwNode.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index 97d69e80b..44450b584 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -18,12 +18,13 @@ * * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; import com.kunzisoft.keepass.app.App; +import com.kunzisoft.keepass.database.ISmallTimeLogger; import org.joda.time.LocalDate; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwVersion.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/PwVersion.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java index 48b337ff9..dd4e9f1b5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwVersion.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element; public enum PwVersion { V3, V4; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java index da3c675ff..34b827c4a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java @@ -19,9 +19,9 @@ */ package com.kunzisoft.keepass.database.iterator; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwEntryV3; -import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.search.SearchParameters; import com.kunzisoft.keepass.database.search.SearchParametersV4; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java index b6d6dbc8d..2c3465ce7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.iterator; -import com.kunzisoft.keepass.database.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.database.search.SearchParameters; import java.util.NoSuchElementException; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java index 3e08817a4..795a752ae 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.iterator; -import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.search.SearchParametersV4; import com.kunzisoft.keepass.database.security.ProtectedString; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java index 36c82ab3e..1fc08ad3c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.load; -import com.kunzisoft.keepass.database.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java index 02cefcb7e..d2ebef187 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java @@ -19,8 +19,8 @@ */ package com.kunzisoft.keepass.database.load; -import com.kunzisoft.keepass.database.PwDbHeaderV3; -import com.kunzisoft.keepass.database.PwDbHeaderV4; +import com.kunzisoft.keepass.database.element.PwDbHeaderV3; +import com.kunzisoft.keepass.database.element.PwDbHeaderV4; import com.kunzisoft.keepass.database.exception.InvalidDBSignatureException; import com.kunzisoft.keepass.stream.LEDataInputStream; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index 99ef87370..b4ed6802d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -49,13 +49,13 @@ import android.util.Log; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.PwDatabaseV3; -import com.kunzisoft.keepass.database.PwDate; -import com.kunzisoft.keepass.database.PwDbHeader; -import com.kunzisoft.keepass.database.PwDbHeaderV3; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; -import com.kunzisoft.keepass.database.PwEntryV3; -import com.kunzisoft.keepass.database.PwGroupV3; +import com.kunzisoft.keepass.database.element.PwDatabaseV3; +import com.kunzisoft.keepass.database.element.PwDate; +import com.kunzisoft.keepass.database.element.PwDbHeader; +import com.kunzisoft.keepass.database.element.PwDbHeaderV3; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.database.exception.InvalidAlgorithmException; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.InvalidDBSignatureException; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java index 9984892db..069c463f2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.load; -import com.kunzisoft.keepass.database.PwDatabaseV3Debug; +import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 42e4c1cdd..2c0cbb36e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -25,15 +25,15 @@ import com.kunzisoft.keepass.crypto.PwStreamCipherFactory; import com.kunzisoft.keepass.crypto.engine.CipherEngine; import com.kunzisoft.keepass.database.ITimeLogger; import com.kunzisoft.keepass.database.PwCompressionAlgorithm; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwDatabaseV4XML; -import com.kunzisoft.keepass.database.PwDate; -import com.kunzisoft.keepass.database.PwDbHeaderV4; -import com.kunzisoft.keepass.database.PwDeletedObject; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwGroupV4; -import com.kunzisoft.keepass.database.PwIconCustom; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwDatabaseV4XML; +import com.kunzisoft.keepass.database.element.PwDate; +import com.kunzisoft.keepass.database.element.PwDbHeaderV4; +import com.kunzisoft.keepass.database.element.PwDeletedObject; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwIconCustom; import com.kunzisoft.keepass.database.exception.ArcFourException; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.InvalidPasswordException; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java index 3d06d7748..687af8b43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.save; -import com.kunzisoft.keepass.database.PwDbHeaderV3; +import com.kunzisoft.keepass.database.element.PwDbHeaderV3; import com.kunzisoft.keepass.stream.LEDataOutputStream; import java.io.IOException; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java index 689eff18e..abeb66e70 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java @@ -21,9 +21,9 @@ package com.kunzisoft.keepass.database.save; import com.kunzisoft.keepass.collections.VariantDictionary; import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwDbHeader; -import com.kunzisoft.keepass.database.PwDbHeaderV4; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwDbHeader; +import com.kunzisoft.keepass.database.element.PwDbHeaderV4; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.stream.HmacBlockStream; import com.kunzisoft.keepass.stream.LEDataOutputStream; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java index a362ab7c0..836330391 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java @@ -19,8 +19,8 @@ */ package com.kunzisoft.keepass.database.save; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwDbHeaderV4; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwDbHeaderV4; import com.kunzisoft.keepass.database.security.ProtectedBinary; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.utils.MemUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java index f4ca0d90d..cdc4769d9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java @@ -19,10 +19,10 @@ */ package com.kunzisoft.keepass.database.save; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwDatabaseV3; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwDbHeader; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDatabaseV3; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwDbHeader; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import java.io.OutputStream; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java index d40330a97..811f1b1d4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java @@ -20,12 +20,12 @@ package com.kunzisoft.keepass.database.save; import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.PwDatabaseV3; -import com.kunzisoft.keepass.database.PwDbHeader; -import com.kunzisoft.keepass.database.PwDbHeaderV3; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; -import com.kunzisoft.keepass.database.PwEntryV3; -import com.kunzisoft.keepass.database.PwGroupV3; +import com.kunzisoft.keepass.database.element.PwDatabaseV3; +import com.kunzisoft.keepass.database.element.PwDbHeader; +import com.kunzisoft.keepass.database.element.PwDbHeaderV3; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.stream.NullOutputStream; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java index 7e1c681c8..a419f0dfb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java @@ -19,9 +19,9 @@ */ package com.kunzisoft.keepass.database.save; -import com.kunzisoft.keepass.database.PwDatabaseV3; -import com.kunzisoft.keepass.database.PwDatabaseV3Debug; -import com.kunzisoft.keepass.database.PwDbHeaderV3; +import com.kunzisoft.keepass.database.element.PwDatabaseV3; +import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; +import com.kunzisoft.keepass.database.element.PwDbHeaderV3; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import java.io.OutputStream; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index d697ae517..453486e71 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -34,14 +34,14 @@ import com.kunzisoft.keepass.database.GroupHandler; import com.kunzisoft.keepass.database.ITimeLogger; import com.kunzisoft.keepass.database.MemoryProtectionConfig; import com.kunzisoft.keepass.database.PwCompressionAlgorithm; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwDatabaseV4XML; -import com.kunzisoft.keepass.database.PwDbHeaderV4; -import com.kunzisoft.keepass.database.PwDefsV4; -import com.kunzisoft.keepass.database.PwDeletedObject; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwGroupV4; -import com.kunzisoft.keepass.database.PwIconCustom; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwDatabaseV4XML; +import com.kunzisoft.keepass.database.element.PwDbHeaderV4; +import com.kunzisoft.keepass.database.element.PwDefsV4; +import com.kunzisoft.keepass.database.element.PwDeletedObject; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwIconCustom; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.database.exception.UnknownKDF; import com.kunzisoft.keepass.database.security.ProtectedBinary; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java index 291b60a46..22ab3b80b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.save; -import com.kunzisoft.keepass.database.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.utils.Types; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java index 9af15826a..ed649818c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.save; -import com.kunzisoft.keepass.database.PwGroupV3; +import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.utils.Types; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java index 99753a8e2..b96201c55 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.search; import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntry; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import java.util.Date; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java index 256bd7d53..6513683d3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.search; import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntry; import java.util.Date; import java.util.List; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index 57deea143..d49fe1ce8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -19,9 +19,9 @@ */ package com.kunzisoft.keepass.database.search; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwGroup; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroup; import com.kunzisoft.keepass.utils.StrUtil; import com.kunzisoft.keepass.utils.UuidUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java index 1faa70801..059760b5d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java @@ -20,8 +20,8 @@ package com.kunzisoft.keepass.database.search; import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.utils.StrUtil; import java.util.ArrayList; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index 288011b10..8ca8bd7bc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -24,15 +24,15 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwDatabaseV3; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwEntryV3; -import com.kunzisoft.keepass.database.PwEntryV4; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.database.PwGroupV3; -import com.kunzisoft.keepass.database.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDatabaseV3; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwGroupV3; +import com.kunzisoft.keepass.database.element.PwGroupV4; import java.util.ArrayList; import java.util.Iterator; diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index f78f0eaa3..c8f6ace74 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -35,9 +35,9 @@ import android.widget.Toast; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; -import com.kunzisoft.keepass.database.PwGroup; -import com.kunzisoft.keepass.database.PwIcon; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwIcon; import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION; import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE; diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java index 83ba32474..8cb52d41f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java @@ -37,7 +37,7 @@ import android.widget.GridView; import android.widget.ImageView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwIconStandard; import com.kunzisoft.keepass.icons.IconPack; import com.kunzisoft.keepass.icons.IconPackChooser; import com.kunzisoft.keepass.stylish.StylishActivity; diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java index 068989a0b..cc85acfab 100644 --- a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java @@ -34,9 +34,9 @@ import android.util.Log; import android.widget.ImageView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.PwIcon; -import com.kunzisoft.keepass.database.PwIconCustom; -import com.kunzisoft.keepass.database.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwIcon; +import com.kunzisoft.keepass.database.element.PwIconCustom; +import com.kunzisoft.keepass.database.element.PwIconStandard; import org.apache.commons.collections.map.AbstractReferenceMap; import org.apache.commons.collections.map.ReferenceMap; diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index cb4529029..eb7d44271 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -63,7 +63,7 @@ import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.compat.ClipDataCompat; -import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java index 8c7d140d9..0a42e5e34 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java @@ -26,7 +26,7 @@ import android.support.v7.preference.PreferenceFragmentCompat; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.database.element.Database; public class MainPreferenceFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index b8e64cf34..a700dc810 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -45,7 +45,7 @@ import com.kunzisoft.keepass.BuildConfig; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.ReadOnlyHelper; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.Database; +import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.dialogs.ProFeatureDialogFragment; import com.kunzisoft.keepass.dialogs.UnavailableFeatureDialogFragment; import com.kunzisoft.keepass.dialogs.UnderDevelopmentFeatureDialogFragment; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java index f06b12189..fcd0d621f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.java @@ -26,7 +26,7 @@ import android.support.v7.widget.RecyclerView; import android.view.View; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter; diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt index 2b5ad2f4a..234016aca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.settings.preferencedialogfragment import android.view.View import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.database.Database +import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.action.SaveDatabaseProgressDialogRunnable import com.kunzisoft.keepass.tasks.ActionRunnable diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java b/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java index c84c76f87..2adb5f012 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.utils; import android.net.Uri; -import com.kunzisoft.keepass.database.PwDate; +import com.kunzisoft.keepass.database.element.PwDate; public class EmptyUtils { public static boolean isNullOrEmpty(String str) { diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java index 2491c416e..b5450adca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java @@ -19,8 +19,8 @@ */ package com.kunzisoft.keepass.utils; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwEntryV4; import java.util.HashMap; import java.util.Map; diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index e80fe7f7e..b0a667322 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -19,10 +19,10 @@ */ package com.kunzisoft.keepass.utils; -import com.kunzisoft.keepass.database.PwDatabase; -import com.kunzisoft.keepass.database.PwDatabaseV4; -import com.kunzisoft.keepass.database.PwEntry; -import com.kunzisoft.keepass.database.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.search.EntrySearchV4; import com.kunzisoft.keepass.database.search.SearchParametersV4; From 20f76751358b9a48197dcb1a63b4263cf0c10cfe Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 15 Mar 2019 16:48:59 +0100 Subject: [PATCH 084/289] Refactor database elements (In PROGRESS) --- .../keepass/tests/PwEntryTestV3.java | 2 +- .../keepass/tests/database/DeleteEntry.java | 24 +- .../keepass/tests/database/EntryV4.java | 8 +- .../keepass/tests/search/SearchTest.java | 8 +- .../keepass/activities/EntryActivity.java | 22 +- .../keepass/activities/EntryEditActivity.java | 36 +-- .../keepass/activities/GroupActivity.java | 94 +++---- .../keepass/activities/ListNodesFragment.java | 20 +- .../keepass/adapters/NodeAdapter.java | 63 ++--- .../adapters/SearchEntryCursorAdapter.java | 10 +- .../keepass/autofill/AutofillHelper.kt | 16 +- .../keepass/database/EntryHandler.java | 4 +- .../keepass/database/SortNodeEnum.java | 80 +++--- .../database/action/node/AddEntryRunnable.kt | 4 +- .../database/action/node/AddGroupRunnable.kt | 4 +- .../node/AfterActionNodeFinishRunnable.kt | 4 +- .../database/action/node/CopyEntryRunnable.kt | 10 +- .../action/node/DeleteEntryRunnable.kt | 8 +- .../action/node/DeleteGroupRunnable.java | 32 ++- .../database/action/node/MoveEntryRunnable.kt | 10 +- .../database/action/node/MoveGroupRunnable.kt | 8 +- .../action/node/UpdateEntryRunnable.kt | 8 +- .../action/node/UpdateGroupRunnable.kt | 8 +- .../keepass/database/cursor/EntryCursor.java | 10 +- .../keepass/database/element/Database.java | 77 +++--- .../keepass/database/element/PwDatabase.java | 81 +++--- .../database/element/PwDatabaseV3.java | 64 ++--- .../database/element/PwDatabaseV4.java | 78 +++--- .../keepass/database/element/PwEntry.java | 234 ------------------ .../database/element/PwEntryInterface.java | 140 +++++++++++ .../keepass/database/element/PwEntryV3.java | 101 ++++++-- .../keepass/database/element/PwEntryV4.java | 83 +++++-- .../keepass/database/element/PwGroup.java | 191 -------------- .../database/element/PwGroupInterface.java | 49 ++++ .../keepass/database/element/PwGroupV3.java | 163 ++++++++++-- .../keepass/database/element/PwGroupV4.java | 151 +++++++++-- .../keepass/database/element/PwNode.java | 112 +++++---- .../element/{PwGroupId.java => PwNodeId.java} | 9 +- .../{PwGroupIdV3.java => PwNodeIdInt.java} | 33 ++- .../{PwGroupIdV4.java => PwNodeIdUUID.java} | 29 ++- .../database/element/PwNodeInterface.java | 48 ++++ .../iterator/EntrySearchStringIterator.java | 6 +- .../iterator/EntrySearchStringIteratorV3.java | 2 +- .../keepass/database/load/ImporterV3.java | 2 +- .../database/save/PwEntryOutputV3.java | 2 +- .../database/search/EntrySearchHandler.java | 8 +- .../search/EntrySearchHandlerAll.java | 4 +- .../database/search/EntrySearchHandlerV4.java | 8 +- .../database/search/SearchDbHelper.java | 33 ++- .../dialogs/GroupEditDialogFragment.java | 4 +- .../kunzisoft/keepass/utils/SprEngineV4.java | 6 +- 51 files changed, 1186 insertions(+), 1025 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java rename app/src/main/java/com/kunzisoft/keepass/database/element/{PwGroupId.java => PwNodeId.java} (82%) rename app/src/main/java/com/kunzisoft/keepass/database/element/{PwGroupIdV3.java => PwNodeIdInt.java} (69%) rename app/src/main/java/com/kunzisoft/keepass/database/element/{PwGroupIdV4.java => PwNodeIdUUID.java} (68%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java index 7cea7bc36..66c647a68 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java @@ -42,7 +42,7 @@ public class PwEntryTestV3 extends AndroidTestCase { } public void testName() { - assertTrue("Name was " + mPE.getTitle(), mPE.getTitle().equals("Amazon")); + assertTrue("Name was " + mPE.getName(), mPE.getName().equals("Amazon")); } public void testPassword() throws UnsupportedEncodingException { diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index 0fa3fa0e9..3c5fd82bc 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -22,13 +22,13 @@ package com.kunzisoft.keepass.tests.database; import android.content.Context; import android.test.AndroidTestCase; +import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwGroup; -import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.search.SearchDbHelper; import java.util.List; @@ -56,7 +56,7 @@ public class DeleteEntry extends AndroidTestCase { } PwDatabaseV3 pm = (PwDatabaseV3) db.getPwDatabase(); - PwGroup group1 = getGroup(pm, GROUP1_NAME); + PwGroupInterface group1 = getGroup(pm, GROUP1_NAME); assertNotNull("Could not find group1", group1); // Delete the group @@ -64,16 +64,16 @@ public class DeleteEntry extends AndroidTestCase { task.run(); // Verify the entries were deleted - PwEntry entry1 = getEntry(pm, ENTRY1_NAME); + PwEntryInterface entry1 = getEntry(pm, ENTRY1_NAME); assertNull("Entry 1 was not removed", entry1); - PwEntry entry2 = getEntry(pm, ENTRY2_NAME); + PwEntryInterface entry2 = getEntry(pm, ENTRY2_NAME); assertNull("Entry 2 was not removed", entry2); // Verify the entries were removed from the search index SearchDbHelper dbHelp = new SearchDbHelper(ctx); - PwGroup results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME, 100); - PwGroup results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME, 100); + PwGroupInterface results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME, 100); + PwGroupInterface results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME, 100); assertEquals("Entry1 was not removed from the search results", 0, results1.numbersOfChildEntries()); assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries()); @@ -88,7 +88,7 @@ public class DeleteEntry extends AndroidTestCase { List entries = pm.getEntries(); for ( int i = 0; i < entries.size(); i++ ) { PwEntryV3 entry = entries.get(i); - if ( entry.getTitle().equals(name) ) { + if ( entry.getName().equals(name) ) { return entry; } } @@ -97,10 +97,10 @@ public class DeleteEntry extends AndroidTestCase { } - private PwGroup getGroup(PwDatabase pm, String name) { - List groups = pm.getGroups(); + private PwGroupInterface getGroup(PwDatabase pm, String name) { + List groups = pm.getGroups(); for ( int i = 0; i < groups.size(); i++ ) { - PwGroup group = groups.get(i); + PwGroupInterface group = groups.get(i); if ( group.getName().equals(name) ) { return group; } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java index d15671cf7..b44e08d61 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java @@ -33,21 +33,21 @@ public class EntryV4 extends TestCase { PwEntryV4 entry = new PwEntryV4(); entry.startToManageFieldReferences(db); - entry.setTitle("Title1"); + entry.setName("Title1"); entry.setUsername("User1"); entry.createBackup(db); - entry.setTitle("Title2"); + entry.setName("Title2"); entry.setUsername("User2"); entry.createBackup(db); - entry.setTitle("Title3"); + entry.setName("Title3"); entry.setUsername("User3"); entry.createBackup(db); PwEntryV4 backup = entry.getHistory().get(0); entry.stopToManageFieldReferences(); - assertEquals("Title2", backup.getTitle()); + assertEquals("Title2", backup.getName()); assertEquals("User2", backup.getUsername()); } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java index d05fd585a..7e5e69a34 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java @@ -26,7 +26,7 @@ import android.preference.PreferenceManager; import android.test.AndroidTestCase; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.tests.database.TestData; public class SearchTest extends AndroidTestCase { @@ -41,21 +41,21 @@ public class SearchTest extends AndroidTestCase { } public void testSearch() { - PwGroup results = mDb.search("Amazon"); + PwGroupInterface results = mDb.search("Amazon"); assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); } public void testBackupIncluded() { updateOmitSetting(false); - PwGroup results = mDb.search("BackupOnly"); + PwGroupInterface results = mDb.search("BackupOnly"); assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); } public void testBackupExcluded() { updateOmitSetting(true); - PwGroup results = mDb.search("BackupOnly"); + PwGroupInterface results = mDb.search("BackupOnly"); assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0); } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index e095cd30c..37c36e0f3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -41,13 +41,13 @@ import android.widget.Toast; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.ExtraFields; -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwEntry; -import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; +import com.kunzisoft.keepass.app.App; +import com.kunzisoft.keepass.database.ExtraFields; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.notifications.NotificationCopyingService; import com.kunzisoft.keepass.notifications.NotificationField; import com.kunzisoft.keepass.settings.PreferencesUtil; @@ -77,7 +77,7 @@ public class EntryActivity extends LockingHideActivity { private EntryContentsView entryContentsView; private Toolbar toolbar; - protected PwEntry mEntry; + protected PwEntryInterface mEntry; private boolean mShowPassword; private ClipboardHelper clipboardHelper; @@ -85,7 +85,7 @@ public class EntryActivity extends LockingHideActivity { private int iconColor; - public static void launch(Activity activity, PwEntry pw, boolean readOnly) { + public static void launch(Activity activity, PwEntryInterface pw, boolean readOnly) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryActivity.class); intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID())); @@ -180,8 +180,8 @@ public class EntryActivity extends LockingHideActivity { // username already copied, waiting for user's action before copy password. Intent intent = new Intent(this, NotificationCopyingService.class); intent.setAction(NotificationCopyingService.ACTION_NEW_NOTIFICATION); - if (mEntry.getTitle() != null) - intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getTitle()); + if (mEntry.getName() != null) + intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getName()); // Construct notification fields ArrayList notificationFields = new ArrayList<>(); // Add username if exists to notifications @@ -316,7 +316,7 @@ public class EntryActivity extends LockingHideActivity { db.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); // Assign title text - titleView.setText(mEntry.getVisualTitle()); + titleView.setText(PwEntryInterface.getVisualTitle(mEntry)); // Assign basic fields entryContentsView.assignUserName(mEntry.getUsername()); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 0b9e04119..1eef01b94 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -42,22 +42,22 @@ import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwDate; -import com.kunzisoft.keepass.database.element.PwEntry; -import com.kunzisoft.keepass.database.element.PwGroup; -import com.kunzisoft.keepass.database.element.PwGroupId; -import com.kunzisoft.keepass.database.element.PwIconStandard; -import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.database.action.node.ActionNodeValues; import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwDate; +import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwNodeId; +import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.PwIconStandard; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; import com.kunzisoft.keepass.settings.PreferencesUtil; +import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.Types; @@ -88,8 +88,8 @@ public class EntryEditActivity extends LockingHideActivity private Database database; - protected PwEntry mEntry; - protected PwEntry mCallbackNewEntry; + protected PwEntryInterface mEntry; + protected PwEntryInterface mCallbackNewEntry; protected boolean mIsNew; protected PwIconStandard mSelectedIconStandard; @@ -114,7 +114,7 @@ public class EntryEditActivity extends LockingHideActivity * @param activity from activity * @param pwEntry Entry to update */ - public static void launch(Activity activity, PwEntry pwEntry) { + public static void launch(Activity activity, PwEntryInterface pwEntry) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryEditActivity.class); intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pwEntry.getUUID())); @@ -128,7 +128,7 @@ public class EntryEditActivity extends LockingHideActivity * @param activity from activity * @param pwGroup Group who will contains new entry */ - public static void launch(Activity activity, PwGroup pwGroup) { + public static void launch(Activity activity, PwGroupInterface pwGroup) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryEditActivity.class); intent.putExtra(KEY_PARENT, pwGroup.getId()); @@ -185,8 +185,8 @@ public class EntryEditActivity extends LockingHideActivity PwDatabase pm = database.getPwDatabase(); if ( uuidBytes == null ) { - PwGroupId parentId = intent.getParcelableExtra(KEY_PARENT); - PwGroup parent = pm.getGroupByGroupId(parentId); + PwNodeId parentId = intent.getParcelableExtra(KEY_PARENT); + PwGroupInterface parent = pm.getGroupByGroupId(parentId); mEntry = database.createEntry(parent); mIsNew = true; // Add the default icon @@ -413,10 +413,10 @@ public class EntryEditActivity extends LockingHideActivity return errorValidation.isValidate; } - protected PwEntry populateNewEntry() { + protected PwEntryInterface populateNewEntry() { PwDatabase db = App.getDB().getPwDatabase(); - PwEntry newEntry = mEntry.clone(); + PwEntryInterface newEntry = mEntry.clone(); newEntry.startToManageFieldReferences(db); @@ -425,7 +425,7 @@ public class EntryEditActivity extends LockingHideActivity newEntry.setLastAccessTime(new PwDate()); newEntry.setLastModificationTime(new PwDate()); - newEntry.setTitle(entryTitleView.getText().toString()); + newEntry.setName(entryTitleView.getText().toString()); newEntry.setIconStandard(retrieveIcon()); newEntry.setUrl(entryUrlView.getText().toString()); @@ -513,7 +513,7 @@ public class EntryEditActivity extends LockingHideActivity // Don't start the field reference manager, we want to see the raw ref mEntry.stopToManageFieldReferences(); - entryTitleView.setText(mEntry.getTitle()); + entryTitleView.setText(mEntry.getName()); entryUserNameView.setText(mEntry.getUsername()); entryUrlView.setText(mEntry.getUrl()); String password = mEntry.getPassword(); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index d22943575..6421d9cb8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -59,15 +59,6 @@ import com.kunzisoft.keepass.adapters.NodeAdapter; import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwEntry; -import com.kunzisoft.keepass.database.element.PwGroup; -import com.kunzisoft.keepass.database.element.PwGroupId; -import com.kunzisoft.keepass.database.element.PwGroupV4; -import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwIconStandard; -import com.kunzisoft.keepass.database.element.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; @@ -80,6 +71,15 @@ import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable; import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable; import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwNodeId; +import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwIcon; +import com.kunzisoft.keepass.database.element.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwNodeInterface; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; @@ -130,17 +130,17 @@ public class GroupActivity extends LockingActivity private ListNodesFragment listNodesFragment; private boolean currentGroupIsASearch; - private PwGroup rootGroup; - private PwGroup mCurrentGroup; - private PwGroup oldGroupToUpdate; - private PwNode nodeToCopy; - private PwNode nodeToMove; + private PwGroupInterface rootGroup; + private PwGroupInterface mCurrentGroup; + private PwGroupInterface oldGroupToUpdate; + private PwNodeInterface nodeToCopy; + private PwNodeInterface nodeToMove; private SearchEntryCursorAdapter searchSuggestionAdapter; private int iconColor; - private static void buildAndLaunchIntent(Activity activity, PwGroup group, boolean readOnly, + private static void buildAndLaunchIntent(Activity activity, PwGroupInterface group, boolean readOnly, IntentBuildLauncher intentBuildLauncher) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, GroupActivity.class); @@ -166,7 +166,7 @@ public class GroupActivity extends LockingActivity launch(activity, null, readOnly); } - public static void launch(Activity activity, PwGroup group, boolean readOnly) { + public static void launch(Activity activity, PwGroupInterface group, boolean readOnly) { TimeoutHelper.INSTANCE.recordTime(activity); buildAndLaunchIntent(activity, group, readOnly, (intent) -> activity.startActivityForResult(intent, 0)); @@ -314,7 +314,7 @@ public class GroupActivity extends LockingActivity } } - private void openSearchGroup(PwGroup group) { + private void openSearchGroup(PwGroupInterface group) { // Delete the previous search fragment Fragment searchFragment = getSupportFragmentManager().findFragmentByTag(SEARCH_FRAGMENT_TAG); if (searchFragment != null) { @@ -326,11 +326,11 @@ public class GroupActivity extends LockingActivity openGroup(group, true); } - private void openChildGroup(PwGroup group) { + private void openChildGroup(PwGroupInterface group) { openGroup(group, false); } - private void openGroup(PwGroup group, boolean isASearch) { + private void openGroup(PwGroupInterface group, boolean isASearch) { // Check TimeoutHelper TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeoutOrResetTimeout(this, () -> { // Open a group in a new fragment @@ -373,7 +373,7 @@ public class GroupActivity extends LockingActivity super.onSaveInstanceState(outState); } - protected @Nullable PwGroup retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { + protected @Nullable PwGroupInterface retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { // If it's a search if ( Intent.ACTION_SEARCH.equals(intent.getAction()) ) { @@ -381,7 +381,7 @@ public class GroupActivity extends LockingActivity } // else a real group else { - PwGroupId pwGroupId = null; + PwNodeId pwGroupId = null; if (savedInstanceState != null && savedInstanceState.containsKey(GROUP_ID_KEY)) { pwGroupId = savedInstanceState.getParcelable(GROUP_ID_KEY); @@ -393,7 +393,7 @@ public class GroupActivity extends LockingActivity setReadOnly(database.isReadOnly() || getReadOnly()); // Force read only if the database is like that Log.w(TAG, "Creating tree view"); - PwGroup currentGroup; + PwGroupInterface currentGroup; if (pwGroupId == null) { currentGroup = rootGroup; } else { @@ -484,18 +484,18 @@ public class GroupActivity extends LockingActivity } @Override - public void onNodeClick(PwNode node) { + public void onNodeClick(PwNodeInterface node) { switch (node.getType()) { case GROUP: try { - openChildGroup((PwGroup) node); + openChildGroup((PwGroupInterface) node); } catch (ClassCastException e) { Log.e(TAG, "Node can't be cast in Group"); } break; case ENTRY: try { - PwEntry entry = ((PwEntry) node); + PwEntryInterface entry = ((PwEntryInterface) node); EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), () -> { EntryActivity.launch(GroupActivity.this, entry, getReadOnly()); @@ -530,29 +530,29 @@ public class GroupActivity extends LockingActivity } @Override - public boolean onOpenMenuClick(PwNode node) { + public boolean onOpenMenuClick(PwNodeInterface node) { onNodeClick(node); return true; } @Override - public boolean onEditMenuClick(PwNode node) { + public boolean onEditMenuClick(PwNodeInterface node) { switch (node.getType()) { case GROUP: - oldGroupToUpdate = (PwGroup) node; + oldGroupToUpdate = (PwGroupInterface) node; GroupEditDialogFragment.build(oldGroupToUpdate) .show(getSupportFragmentManager(), GroupEditDialogFragment.TAG_CREATE_GROUP); break; case ENTRY: - EntryEditActivity.launch(GroupActivity.this, (PwEntry) node); + EntryEditActivity.launch(GroupActivity.this, (PwEntryInterface) node); break; } return true; } @Override - public boolean onCopyMenuClick(PwNode node) { + public boolean onCopyMenuClick(PwNodeInterface node) { toolbarPasteExpandableLayout.expand(); nodeToCopy = node; @@ -573,7 +573,7 @@ public class GroupActivity extends LockingActivity Log.e(TAG, "Copy not allowed for group"); break; case ENTRY: - copyEntry((PwEntry) nodeToCopy, mCurrentGroup); + copyEntry((PwEntryInterface) nodeToCopy, mCurrentGroup); break; } nodeToCopy = null; @@ -583,7 +583,7 @@ public class GroupActivity extends LockingActivity } } - private void copyEntry(PwEntry entryToCopy, PwGroup newParent) { + private void copyEntry(PwEntryInterface entryToCopy, PwGroupInterface newParent) { new Thread(new CopyEntryRunnable(this, App.getDB(), entryToCopy, @@ -594,7 +594,7 @@ public class GroupActivity extends LockingActivity } @Override - public boolean onMoveMenuClick(PwNode node) { + public boolean onMoveMenuClick(PwNodeInterface node) { toolbarPasteExpandableLayout.expand(); nodeToMove = node; @@ -612,10 +612,10 @@ public class GroupActivity extends LockingActivity case R.id.menu_paste: switch (nodeToMove.getType()) { case GROUP: - moveGroup((PwGroup) nodeToMove, mCurrentGroup); + moveGroup((PwGroupInterface) nodeToMove, mCurrentGroup); break; case ENTRY: - moveEntry((PwEntry) nodeToMove, mCurrentGroup); + moveEntry((PwEntryInterface) nodeToMove, mCurrentGroup); break; } nodeToMove = null; @@ -625,7 +625,7 @@ public class GroupActivity extends LockingActivity } } - private void moveGroup(PwGroup groupToMove, PwGroup newParent) { + private void moveGroup(PwGroupInterface groupToMove, PwGroupInterface newParent) { new Thread(new MoveGroupRunnable( this, App.getDB(), @@ -636,7 +636,7 @@ public class GroupActivity extends LockingActivity ).start(); } - private void moveEntry(PwEntry entryToMove, PwGroup newParent) { + private void moveEntry(PwEntryInterface entryToMove, PwGroupInterface newParent) { new Thread(new MoveEntryRunnable( this, App.getDB(), @@ -648,19 +648,19 @@ public class GroupActivity extends LockingActivity } @Override - public boolean onDeleteMenuClick(PwNode node) { + public boolean onDeleteMenuClick(PwNodeInterface node) { switch (node.getType()) { case GROUP: - deleteGroup((PwGroup) node); + deleteGroup((PwGroupInterface) node); break; case ENTRY: - deleteEntry((PwEntry) node); + deleteEntry((PwEntryInterface) node); break; } return true; } - private void deleteGroup(PwGroup group) { + private void deleteGroup(PwGroupInterface group) { //TODO Verify trash recycle bin new Thread(new DeleteGroupRunnable( this, @@ -671,7 +671,7 @@ public class GroupActivity extends LockingActivity ).start(); } - private void deleteEntry(PwEntry entry) { + private void deleteEntry(PwEntryInterface entry) { new Thread(new DeleteEntryRunnable( this, App.getDB(), @@ -962,7 +962,7 @@ public class GroupActivity extends LockingActivity case CREATION: // If group creation // Build the group - PwGroup newGroup = database.createGroup(mCurrentGroup); + PwGroupInterface newGroup = database.createGroup(mCurrentGroup); newGroup.setName(name); try { iconStandard = (PwIconStandard) icon; @@ -981,10 +981,10 @@ public class GroupActivity extends LockingActivity case UPDATE: // If update add new elements if (oldGroupToUpdate != null) { - PwGroup updateGroup = oldGroupToUpdate.clone(); + PwGroupInterface updateGroup = oldGroupToUpdate.duplicate(); try { iconStandard = (PwIconStandard) icon; - updateGroup = ((PwGroupV4) oldGroupToUpdate).clone(); // TODO generalize + updateGroup = ((PwGroupV4) oldGroupToUpdate).duplicate(); // TODO generalize } catch (Exception e) { e.printStackTrace(); } @@ -1046,12 +1046,12 @@ public class GroupActivity extends LockingActivity listNodesFragment.removeNode(actionNodeValues.getOldNode()); if (actionNodeValues.getOldNode() != null) { - PwGroup parent = actionNodeValues.getOldNode().getParent(); + PwGroupInterface parent = actionNodeValues.getOldNode().getParent(); Database db = App.getDB(); PwDatabase database = db.getPwDatabase(); if (db.isRecycleBinAvailable() && db.isRecycleBinEnabled()) { - PwGroup recycleBin = database.getRecycleBin(); + PwGroupInterface recycleBin = database.getRecycleBin(); // Add trash if it doesn't exists if (parent.equals(recycleBin) && mCurrentGroup != null diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java index 32bee8ea3..fb58682c1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java @@ -20,10 +20,10 @@ import android.view.ViewGroup; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.adapters.NodeAdapter; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwGroup; -import com.kunzisoft.keepass.database.element.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.PwNodeInterface; import com.kunzisoft.keepass.dialogs.SortDialogFragment; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishFragment; @@ -41,7 +41,7 @@ public class ListNodesFragment extends StylishFragment implements private OnScrollListener onScrollListener; private RecyclerView listView; - private PwGroup currentGroup; + private PwGroupInterface currentGroup; private NodeAdapter mAdapter; private View notFoundView; @@ -52,7 +52,7 @@ public class ListNodesFragment extends StylishFragment implements private boolean readOnly; - public static ListNodesFragment newInstance(PwGroup group, boolean readOnly, boolean isASearch) { + public static ListNodesFragment newInstance(PwGroupInterface group, boolean readOnly, boolean isASearch) { Bundle bundle = new Bundle(); if (group != null) { bundle.putParcelable(GROUP_KEY, group); @@ -248,7 +248,7 @@ public class ListNodesFragment extends StylishFragment implements case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE: if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE || resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { - PwNode newNode = data.getParcelableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY); + PwNodeInterface newNode = data.getParcelableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY); if (newNode != null) { if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE) mAdapter.addNode(newNode); @@ -268,19 +268,19 @@ public class ListNodesFragment extends StylishFragment implements return mAdapter == null || mAdapter.getItemCount() <= 0; } - public void addNode(PwNode newNode) { + public void addNode(PwNodeInterface newNode) { mAdapter.addNode(newNode); } - public void updateNode(PwNode oldNode, PwNode newNode) { + public void updateNode(PwNodeInterface oldNode, PwNodeInterface newNode) { mAdapter.updateNode(oldNode, newNode); } - public void removeNode(PwNode pwNode) { + public void removeNode(PwNodeInterface pwNode) { mAdapter.removeNode(pwNode); } - public PwGroup getMainGroup() { + public PwGroupInterface getMainGroup() { return currentGroup; } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 9bdd9ab87..f8652a602 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -37,18 +37,18 @@ import android.widget.Toast; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwEntry; -import com.kunzisoft.keepass.database.element.PwGroup; -import com.kunzisoft.keepass.database.element.PwNode; import com.kunzisoft.keepass.database.SortNodeEnum; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.PwNodeInterface; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.utils.Util; public class NodeAdapter extends RecyclerView.Adapter { private static final String TAG = NodeAdapter.class.getName(); - private SortedList nodeSortedList; + private SortedList nodeSortedList; private Context context; private LayoutInflater inflater; @@ -85,16 +85,17 @@ public class NodeAdapter extends RecyclerView.Adapter { this.readOnly = false; this.isASearchResult = false; - this.nodeSortedList = new SortedList<>(PwNode.class, new SortedListAdapterCallback(this) { - @Override public int compare(PwNode item1, PwNode item2) { + this.nodeSortedList = new SortedList<>(PwNodeInterface.class, new SortedListAdapterCallback(this) { + @Override public int compare(PwNodeInterface item1, PwNodeInterface item2) { return listSort.getNodeComparator(ascendingSort, groupsBeforeSort).compare(item1, item2); } - @Override public boolean areContentsTheSame(PwNode oldItem, PwNode newItem) { - return oldItem.isContentVisuallyTheSame(newItem); + @Override public boolean areContentsTheSame(PwNodeInterface oldItem, PwNodeInterface newItem) { + return oldItem.getName().equals(newItem.getName()) + && oldItem.getIcon().equals(newItem.getIcon()); } - @Override public boolean areItemsTheSame(PwNode item1, PwNode item2) { + @Override public boolean areItemsTheSame(PwNodeInterface item1, PwNodeInterface item2) { return item1.equals(item2); } }); @@ -142,7 +143,7 @@ public class NodeAdapter extends RecyclerView.Adapter { /** * Rebuild the list by clear and build children from the group */ - public void rebuildList(PwGroup group) { + public void rebuildList(PwGroupInterface group) { this.nodeSortedList.clear(); assignPreferences(); // TODO verify sort @@ -166,7 +167,7 @@ public class NodeAdapter extends RecyclerView.Adapter { * Add a node to the list * @param node Node to add */ - public void addNode(PwNode node) { + public void addNode(PwNodeInterface node) { nodeSortedList.add(node); } @@ -174,7 +175,7 @@ public class NodeAdapter extends RecyclerView.Adapter { * Remove a node in the list * @param node Node to delete */ - public void removeNode(PwNode node) { + public void removeNode(PwNodeInterface node) { nodeSortedList.remove(node); } @@ -183,7 +184,7 @@ public class NodeAdapter extends RecyclerView.Adapter { * @param oldNode Node before the update * @param newNode Node after the update */ - public void updateNode(PwNode oldNode, PwNode newNode) { + public void updateNode(PwNodeInterface oldNode, PwNodeInterface newNode) { nodeSortedList.beginBatchedUpdates(); nodeSortedList.remove(oldNode); nodeSortedList.add(newNode); @@ -209,7 +210,7 @@ public class NodeAdapter extends RecyclerView.Adapter { public BasicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { BasicViewHolder basicViewHolder; View view; - if (viewType == PwNode.Type.GROUP.ordinal()) { + if (viewType == PwNodeInterface.Type.GROUP.ordinal()) { view = inflater.inflate(R.layout.list_nodes_group, parent, false); basicViewHolder = new GroupViewHolder(view); } else { @@ -221,7 +222,7 @@ public class NodeAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(@NonNull BasicViewHolder holder, int position) { - PwNode subNode = nodeSortedList.get(position); + PwNodeInterface subNode = nodeSortedList.get(position); // Assign image int iconColor = Color.BLACK; switch (subNode.getType()) { @@ -234,7 +235,7 @@ public class NodeAdapter extends RecyclerView.Adapter { } database.getDrawFactory().assignDatabaseIconTo(context, holder.icon, subNode.getIcon(), iconColor); // Assign text - holder.text.setText(subNode.getTitle()); + holder.text.setText(subNode.getName()); // Assign click holder.container.setOnClickListener( new OnNodeClickListener(subNode)); @@ -247,11 +248,11 @@ public class NodeAdapter extends RecyclerView.Adapter { // Add username holder.subText.setText(""); holder.subText.setVisibility(View.GONE); - if (subNode.getType().equals(PwNode.Type.ENTRY)) { - PwEntry entry = (PwEntry) subNode; + if (subNode.getType().equals(PwNodeInterface.Type.ENTRY)) { + PwEntryInterface entry = (PwEntryInterface) subNode; entry.startToManageFieldReferences(database.getPwDatabase()); - holder.text.setText(entry.getVisualTitle()); + holder.text.setText(PwEntryInterface.getVisualTitle(entry)); String username = entry.getUsername(); if (showUsernames && !username.isEmpty()) { @@ -293,27 +294,27 @@ public class NodeAdapter extends RecyclerView.Adapter { * Callback listener to redefine to do an action when a node is click */ public interface NodeClickCallback { - void onNodeClick(PwNode node); + void onNodeClick(PwNodeInterface node); } /** * Menu listener to redefine to do an action in menu */ public interface NodeMenuListener { - boolean onOpenMenuClick(PwNode node); - boolean onEditMenuClick(PwNode node); - boolean onCopyMenuClick(PwNode node); - boolean onMoveMenuClick(PwNode node); - boolean onDeleteMenuClick(PwNode node); + boolean onOpenMenuClick(PwNodeInterface node); + boolean onEditMenuClick(PwNodeInterface node); + boolean onCopyMenuClick(PwNodeInterface node); + boolean onMoveMenuClick(PwNodeInterface node); + boolean onDeleteMenuClick(PwNodeInterface node); } /** * Utility class for node listener */ private class OnNodeClickListener implements View.OnClickListener { - private PwNode node; + private PwNodeInterface node; - OnNodeClickListener(PwNode node) { + OnNodeClickListener(PwNodeInterface node) { this.node = node; } @@ -329,11 +330,11 @@ public class NodeAdapter extends RecyclerView.Adapter { */ private class ContextMenuBuilder implements View.OnCreateContextMenuListener { - private PwNode node; + private PwNodeInterface node; private NodeMenuListener menuListener; private boolean readOnly; - ContextMenuBuilder(PwNode node, NodeMenuListener menuListener, boolean readOnly) { + ContextMenuBuilder(PwNodeInterface node, NodeMenuListener menuListener, boolean readOnly) { this.menuListener = menuListener; this.node = node; this.readOnly = readOnly; @@ -359,7 +360,7 @@ public class NodeAdapter extends RecyclerView.Adapter { if (readOnly || isASearchResult || node.equals(App.getDB().getPwDatabase().getRecycleBin()) - || node.getType().equals(PwNode.Type.GROUP)) { + || node.getType().equals(PwNodeInterface.Type.GROUP)) { // TODO COPY For Group contextMenu.removeItem(R.id.menu_copy); } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java index 363553a34..bcaa6ca5d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java @@ -31,11 +31,11 @@ import android.widget.ImageView; import android.widget.TextView; import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.database.cursor.EntryCursor; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwIcon; import com.kunzisoft.keepass.database.element.PwIconFactory; -import com.kunzisoft.keepass.database.cursor.EntryCursor; import com.kunzisoft.keepass.settings.PreferencesUtil; import java.util.UUID; @@ -104,7 +104,7 @@ public class SearchEntryCursorAdapter extends CursorAdapter { database.getDrawFactory().assignDatabaseIconTo(context, viewHolder.imageViewIcon, icon, iconColor); // Assign title - String showTitle = PwEntry.getVisualTitle(false, title, username, url, uuid); + String showTitle = PwEntryInterface.getVisualTitle(false, title, username, url, uuid); viewHolder.textViewTitle.setText(showTitle); if (displayUsername && !username.isEmpty()) { viewHolder.textViewSubTitle.setText(String.format("(%s)", username)); @@ -124,8 +124,8 @@ public class SearchEntryCursorAdapter extends CursorAdapter { return database.searchEntry(constraint.toString()); } - public PwEntry getEntryFromPosition(int position) { - PwEntry pwEntry = null; + public PwEntryInterface getEntryFromPosition(int position) { + PwEntryInterface pwEntry = null; Cursor cursor = this.getCursor(); if (cursor.moveToFirst() diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt index e04dffa17..c126925d4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -33,7 +33,7 @@ import android.view.autofill.AutofillValue import android.widget.RemoteViews import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.EntrySelectionHelper -import com.kunzisoft.keepass.database.element.PwEntry +import com.kunzisoft.keepass.database.element.PwEntryInterface import java.util.* @@ -51,18 +51,18 @@ object AutofillHelper { return null } - private fun makeEntryTitle(entry: PwEntry<*>): String { - if (!entry.title.isEmpty() && !entry.username.isEmpty()) - return String.format("%s (%s)", entry.title, entry.username) - if (!entry.title.isEmpty()) - return entry.title + private fun makeEntryTitle(entry: PwEntryInterface): String { + if (!entry.name.isEmpty() && !entry.username.isEmpty()) + return String.format("%s (%s)", entry.name, entry.username) + if (!entry.name.isEmpty()) + return entry.name if (!entry.username.isEmpty()) return entry.username return if (!entry.notes.isEmpty()) entry.notes.trim { it <= ' ' } else "" // TODO No title } - private fun buildDataset(context: Context, entry: PwEntry<*>, + private fun buildDataset(context: Context, entry: PwEntryInterface, struct: StructureParser.Result): Dataset? { val title = makeEntryTitle(entry) val views = newRemoteViews(context.packageName, title) @@ -91,7 +91,7 @@ object AutofillHelper { /** * Method to hit when right key is selected */ - fun buildResponseWhenEntrySelected(activity: Activity, entry: PwEntry<*>) { + fun buildResponseWhenEntrySelected(activity: Activity, entry: PwEntryInterface) { val mReplyIntent: Intent activity.intent?.let { intent -> if (intent.extras.containsKey(ASSIST_STRUCTURE)) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java index f8adf1f75..ddd3f5906 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java @@ -19,14 +19,14 @@ */ package com.kunzisoft.keepass.database; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; /** "Delegate" class for operating on each entry when traversing all of * them * @author bpellin * */ -public abstract class EntryHandler { +public abstract class EntryHandler { public abstract boolean operate(T entry); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java index 923139dd1..dc2f46267 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java @@ -20,16 +20,16 @@ package com.kunzisoft.keepass.database; -import com.kunzisoft.keepass.database.element.PwEntry; -import com.kunzisoft.keepass.database.element.PwGroup; -import com.kunzisoft.keepass.database.element.PwNode; +import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.PwNodeInterface; import java.util.Comparator; public enum SortNodeEnum { DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME; - public Comparator getNodeComparator(boolean ascending, boolean groupsBefore) { + public Comparator getNodeComparator(boolean ascending, boolean groupsBefore) { switch (this) { case DB: return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort @@ -47,7 +47,7 @@ public enum SortNodeEnum { } } - private static abstract class NodeComparator implements Comparator { + private static abstract class NodeComparator implements Comparator { boolean ascending; boolean groupsBefore; @@ -56,19 +56,19 @@ public enum SortNodeEnum { this.groupsBefore = groupsBefore; } - int compareWith(Comparator comparatorGroup, - Comparator comparatorEntry, - PwNode object1, - PwNode object2, + int compareWith(Comparator comparatorGroup, + Comparator comparatorEntry, + PwNodeInterface object1, + PwNodeInterface object2, int resultOfNodeMethodCompare) { if (object1.equals(object2)) return 0; - if (object1 instanceof PwGroup) { - if (object2 instanceof PwGroup) { + if (object1 instanceof PwGroupInterface) { + if (object2 instanceof PwGroupInterface) { return comparatorGroup - .compare((PwGroup) object1, (PwGroup) object2); - } else if (object2 instanceof PwEntry) { + .compare((PwGroupInterface) object1, (PwGroupInterface) object2); + } else if (object2 instanceof PwEntryInterface) { if(groupsBefore) return -1; else @@ -76,11 +76,11 @@ public enum SortNodeEnum { } else { return -1; } - } else if (object1 instanceof PwEntry) { - if(object2 instanceof PwEntry) { + } else if (object1 instanceof PwEntryInterface) { + if(object2 instanceof PwEntryInterface) { return comparatorEntry - .compare((PwEntry) object1, (PwEntry) object2); - } else if (object2 instanceof PwGroup) { + .compare((PwEntryInterface) object1, (PwEntryInterface) object2); + } else if (object2 instanceof PwGroupInterface) { if(groupsBefore) return 1; else @@ -106,15 +106,15 @@ public enum SortNodeEnum { super(ascending, groupsBefore); } - public int compare(PwNode object1, PwNode object2) { + public int compare(PwNodeInterface object1, PwNodeInterface object2) { return compareWith( new GroupNameComparator(ascending), new EntryNameComparator(ascending), object1, object2, - object1.getTitle() - .compareToIgnoreCase(object2.getTitle())); + object1.getName() + .compareToIgnoreCase(object2.getName())); } } @@ -129,7 +129,7 @@ public enum SortNodeEnum { } @Override - public int compare(PwNode object1, PwNode object2) { + public int compare(PwNodeInterface object1, PwNodeInterface object2) { return compareWith( new GroupCreationComparator(ascending), @@ -151,7 +151,7 @@ public enum SortNodeEnum { } @Override - public int compare(PwNode object1, PwNode object2) { + public int compare(PwNodeInterface object1, PwNodeInterface object2) { return compareWith( new GroupLastModificationComparator(ascending), @@ -173,7 +173,7 @@ public enum SortNodeEnum { } @Override - public int compare(PwNode object1, PwNode object2) { + public int compare(PwNodeInterface object1, PwNodeInterface object2) { return compareWith( new GroupLastAccessComparator(ascending), @@ -205,13 +205,13 @@ public enum SortNodeEnum { /** * Group comparator by name */ - public static class GroupNameComparator extends AscendingComparator { + public static class GroupNameComparator extends AscendingComparator { GroupNameComparator(boolean ascending) { super(ascending); } - public int compare(PwGroup object1, PwGroup object2) { + public int compare(PwGroupInterface object1, PwGroupInterface object2) { if (object1.equals(object2)) return 0; @@ -228,13 +228,13 @@ public enum SortNodeEnum { /** * Group comparator by name */ - public static class GroupCreationComparator extends AscendingComparator { + public static class GroupCreationComparator extends AscendingComparator { GroupCreationComparator(boolean ascending) { super(ascending); } - public int compare(PwGroup object1, PwGroup object2) { + public int compare(PwGroupInterface object1, PwGroupInterface object2) { if (object1.equals(object2)) return 0; @@ -252,13 +252,13 @@ public enum SortNodeEnum { /** * Group comparator by last modification */ - public static class GroupLastModificationComparator extends AscendingComparator { + public static class GroupLastModificationComparator extends AscendingComparator { GroupLastModificationComparator(boolean ascending) { super(ascending); } - public int compare(PwGroup object1, PwGroup object2) { + public int compare(PwGroupInterface object1, PwGroupInterface object2) { if (object1.equals(object2)) return 0; @@ -276,13 +276,13 @@ public enum SortNodeEnum { /** * Group comparator by last access */ - public static class GroupLastAccessComparator extends AscendingComparator { + public static class GroupLastAccessComparator extends AscendingComparator { GroupLastAccessComparator(boolean ascending) { super(ascending); } - public int compare(PwGroup object1, PwGroup object2) { + public int compare(PwGroupInterface object1, PwGroupInterface object2) { if (object1.equals(object2)) return 0; @@ -300,17 +300,17 @@ public enum SortNodeEnum { /** * Comparator of Entry by Name */ - public static class EntryNameComparator extends AscendingComparator { + public static class EntryNameComparator extends AscendingComparator { EntryNameComparator(boolean ascending) { super(ascending); } - public int compare(PwEntry object1, PwEntry object2) { + public int compare(PwEntryInterface object1, PwEntryInterface object2) { if (object1.equals(object2)) return 0; - int entryTitleComp = object1.getTitle().compareToIgnoreCase(object2.getTitle()); + int entryTitleComp = object1.getName().compareToIgnoreCase(object2.getName()); // If same title, can be different if (entryTitleComp == 0) { return object1.hashCode() - object2.hashCode(); @@ -323,13 +323,13 @@ public enum SortNodeEnum { /** * Comparator of Entry by Creation */ - public static class EntryCreationComparator extends AscendingComparator { + public static class EntryCreationComparator extends AscendingComparator { EntryCreationComparator(boolean ascending) { super(ascending); } - public int compare(PwEntry object1, PwEntry object2) { + public int compare(PwEntryInterface object1, PwEntryInterface object2) { if (object1.equals(object2)) return 0; @@ -347,13 +347,13 @@ public enum SortNodeEnum { /** * Comparator of Entry by Last Modification */ - public static class EntryLastModificationComparator extends AscendingComparator { + public static class EntryLastModificationComparator extends AscendingComparator { EntryLastModificationComparator(boolean ascending) { super(ascending); } - public int compare(PwEntry object1, PwEntry object2) { + public int compare(PwEntryInterface object1, PwEntryInterface object2) { if (object1.equals(object2)) return 0; @@ -371,13 +371,13 @@ public enum SortNodeEnum { /** * Comparator of Entry by Last Access */ - public static class EntryLastAccessComparator extends AscendingComparator { + public static class EntryLastAccessComparator extends AscendingComparator { EntryLastAccessComparator(boolean ascending) { super(ascending); } - public int compare(PwEntry object1, PwEntry object2) { + public int compare(PwEntryInterface object1, PwEntryInterface object2) { if (object1.equals(object2)) return 0; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt index 74cc8a6b9..edbca87f2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt @@ -21,12 +21,12 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntry +import com.kunzisoft.keepass.database.element.PwEntryInterface class AddEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mNewEntry: PwEntry<*>, + private val mNewEntry: PwEntryInterface, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt index 6e860ae71..79c2ddfc5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt @@ -21,12 +21,12 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwGroup +import com.kunzisoft.keepass.database.element.PwGroupInterface class AddGroupRunnable constructor( context: FragmentActivity, database: Database, - private val mNewGroup: PwGroup<*, *>, + private val mNewGroup: PwGroupInterface, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt index 886b50f8a..1ff76ece1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.action.node -import com.kunzisoft.keepass.database.element.PwNode +import com.kunzisoft.keepass.database.element.PwNodeInterface /** * Callback method who return the node(s) modified after an action @@ -29,7 +29,7 @@ import com.kunzisoft.keepass.database.element.PwNode * - Move : @param oldNode NULL, @param NodeToMove * - Update : @param oldNode NodeToUpdate, @param NodeUpdated */ -data class ActionNodeValues(val success: Boolean, val message: String?, val oldNode: PwNode<*>?, val newNode: PwNode<*>?) +data class ActionNodeValues(val success: Boolean, val message: String?, val oldNode: PwNodeInterface?, val newNode: PwNodeInterface?) abstract class AfterActionNodeFinishRunnable { abstract fun onActionNodeFinish(actionNodeValues: ActionNodeValues) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt index 6b8b1c46e..680424625 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt @@ -22,19 +22,19 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import android.util.Log import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntry -import com.kunzisoft.keepass.database.element.PwGroup +import com.kunzisoft.keepass.database.element.PwEntryInterface +import com.kunzisoft.keepass.database.element.PwGroupInterface class CopyEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mEntryToCopy: PwEntry<*>, - private val mNewParent: PwGroup<*, *>, + private val mEntryToCopy: PwEntryInterface, + private val mNewParent: PwGroupInterface, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { - private var mEntryCopied: PwEntry<*>? = null + private var mEntryCopied: PwEntryInterface? = null override fun nodeAction() { // Update entry with new values diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt index 401ab21fd..8bb6fbb18 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt @@ -21,18 +21,18 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntry -import com.kunzisoft.keepass.database.element.PwGroup +import com.kunzisoft.keepass.database.element.PwEntryInterface +import com.kunzisoft.keepass.database.element.PwGroupInterface class DeleteEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mEntryToDelete: PwEntry<*>, + private val mEntryToDelete: PwEntryInterface, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { - private var mParent: PwGroup<*, *>? = null + private var mParent: PwGroupInterface? = null private var mRecycle: Boolean = false diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java index 89113bfb3..273c9e33e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java @@ -22,8 +22,8 @@ package com.kunzisoft.keepass.database.action.node; import android.support.v4.app.FragmentActivity; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwEntry; -import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,11 +34,15 @@ import java.util.List; // TODO Kotlinized public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { - private PwGroup mGroupToDelete; - private PwGroup mParent; + private PwGroupInterface mGroupToDelete; + private PwGroupInterface mParent; private boolean mRecycle; - public DeleteGroupRunnable(FragmentActivity context, Database database, PwGroup group, AfterActionNodeFinishRunnable finish, boolean save) { + public DeleteGroupRunnable(FragmentActivity context, + Database database, + PwGroupInterface group, + AfterActionNodeFinishRunnable finish, + boolean save) { super(context, database, finish, save); mGroupToDelete = group; } @@ -55,16 +59,26 @@ public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { else { // TODO tests // Remove child entries - List childEnt = new ArrayList<>(mGroupToDelete.getChildEntries()); // TODO new Methods + List childEnt = new ArrayList<>(mGroupToDelete.getChildEntries()); // TODO new Methods for ( int i = 0; i < childEnt.size(); i++ ) { - DeleteEntryRunnable task = new DeleteEntryRunnable((FragmentActivity) getContext(), getDatabase(), childEnt.get(i), null, true); + DeleteEntryRunnable task = new DeleteEntryRunnable( + (FragmentActivity) getContext(), + getDatabase(), + childEnt.get(i), + null, + true); task.run(); } // Remove child groups - List childGrp = new ArrayList<>(mGroupToDelete.getChildGroups()); + List childGrp = new ArrayList<>(mGroupToDelete.getChildGroups()); for ( int i = 0; i < childGrp.size(); i++ ) { - DeleteGroupRunnable task = new DeleteGroupRunnable((FragmentActivity) getContext(), getDatabase(), childGrp.get(i), null, true); + DeleteGroupRunnable task = new DeleteGroupRunnable( + (FragmentActivity) getContext(), + getDatabase(), + childGrp.get(i), + null, + true); task.run(); } getDatabase().deleteGroup(mGroupToDelete); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt index c04f4abd1..f76732a7b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt @@ -22,19 +22,19 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import android.util.Log import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntry -import com.kunzisoft.keepass.database.element.PwGroup +import com.kunzisoft.keepass.database.element.PwEntryInterface +import com.kunzisoft.keepass.database.element.PwGroupInterface class MoveEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mEntryToMove: PwEntry<*>?, - private val mNewParent: PwGroup<*, *>, + private val mEntryToMove: PwEntryInterface?, + private val mNewParent: PwGroupInterface, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { - private var mOldParent: PwGroup<*, *>? = null + private var mOldParent: PwGroupInterface? = null override fun nodeAction() { // Move entry in new parent diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt index cbf4bd0fa..f5f0aa5b7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt @@ -23,18 +23,18 @@ import android.support.v4.app.FragmentActivity import android.util.Log import com.kunzisoft.keepass.R import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwGroup +import com.kunzisoft.keepass.database.element.PwGroupInterface class MoveGroupRunnable constructor( context: FragmentActivity, database: Database, - private val mGroupToMove: PwGroup<*, *>?, - private val mNewParent: PwGroup<*, *>, + private val mGroupToMove: PwGroupInterface?, + private val mNewParent: PwGroupInterface, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { - private var mOldParent: PwGroup<*, *>? = null + private var mOldParent: PwGroupInterface? = null override fun nodeAction() { mGroupToMove?.let { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt index 5dad29f64..74e81ddf5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt @@ -21,19 +21,19 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntry +import com.kunzisoft.keepass.database.element.PwEntryInterface class UpdateEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mOldEntry: PwEntry<*>, - private val mNewEntry: PwEntry<*>, + private val mOldEntry: PwEntryInterface, + private val mNewEntry: PwEntryInterface, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { // Keep backup of original values in case save fails - private val mBackupEntry: PwEntry<*> = mOldEntry.clone() + private val mBackupEntry: PwEntryInterface = mOldEntry.duplicate() override fun nodeAction() { // Update entry with new values diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt index a68fc438f..eb4e9e326 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt @@ -21,19 +21,19 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwGroup +import com.kunzisoft.keepass.database.element.PwGroupInterface class UpdateGroupRunnable constructor( context: FragmentActivity, database: Database, - private val mOldGroup: PwGroup<*, *>, - private val mNewGroup: PwGroup<*, *>, + private val mOldGroup: PwGroupInterface, + private val mNewGroup: PwGroupInterface, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { // Keep backup of original values in case save fails - private val mBackupGroup: PwGroup<*, *> = mOldGroup.clone() + private val mBackupGroup: PwGroupInterface = mOldGroup.duplicate() override fun nodeAction() { // Update group with new values diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java index 2adcc7e77..358b96c8f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java @@ -4,7 +4,7 @@ import android.database.MatrixCursor; import android.provider.BaseColumns; import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.element.PwIconCustom; @@ -50,7 +50,7 @@ public class EntryCursor extends MatrixCursor { addRow(new Object[] {entryId, entry.getUUID().getMostSignificantBits(), entry.getUUID().getLeastSignificantBits(), - entry.getTitle(), + entry.getName(), entry.getIconStandard().getIconId(), PwDatabase.UUID_ZERO.getMostSignificantBits(), PwDatabase.UUID_ZERO.getLeastSignificantBits(), @@ -65,7 +65,7 @@ public class EntryCursor extends MatrixCursor { addRow(new Object[] {entryId, entry.getUUID().getMostSignificantBits(), entry.getUUID().getLeastSignificantBits(), - entry.getTitle(), + entry.getName(), entry.getIconStandard().getIconId(), entry.getIconCustom().getUUID().getMostSignificantBits(), entry.getIconCustom().getUUID().getLeastSignificantBits(), @@ -81,11 +81,11 @@ public class EntryCursor extends MatrixCursor { entryId++; } - private void populateEntryBaseVersion(PwEntry pwEntry, PwIconFactory iconFactory) { + private void populateEntryBaseVersion(PwEntryInterface pwEntry, PwIconFactory iconFactory) { pwEntry.setUUID( new UUID(getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS)))); - pwEntry.setTitle(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE))); + pwEntry.setName(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE))); PwIconStandard iconStandard = iconFactory.getIcon(getInt(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_STANDARD))); pwEntry.setIconStandard(iconStandard); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 9f043b720..570558ec0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -175,11 +175,11 @@ public class Database { } } - public PwGroup search(String str) { + public PwGroupInterface search(String str) { return search(str, Integer.MAX_VALUE); } - public PwGroup search(String str, int max) { + public PwGroupInterface search(String str, int max) { if (searchHelper == null) { return null; } try { switch (pwDatabase.getVersion()) { @@ -199,11 +199,11 @@ public class Database { // TODO real content provider if (!query.isEmpty()) { - PwGroup searchResult = search(query, 6); + PwGroupInterface searchResult = search(query, 6); PwVersion version = getPwDatabase().getVersion(); if (searchResult != null) { for (int i = 0; i < searchResult.numbersOfChildEntries(); i++) { - PwEntry entry = searchResult.getChildEntryAt(i); + PwEntryInterface entry = searchResult.getChildEntryAt(i); if (!entry.isMetaStream()) { // TODO metastream try { switch (version) { @@ -223,7 +223,7 @@ public class Database { return cursor; } - public void populateEntry(PwEntry pwEntry, EntryCursor cursor) { + public void populateEntry(PwEntryInterface pwEntry, EntryCursor cursor) { PwIconFactory iconFactory = getPwDatabase().getIconFactory(); try { switch (getPwDatabase().getVersion()) { @@ -522,11 +522,11 @@ public class Database { } } - public PwEntry createEntry() { + public PwEntryInterface createEntry() { return createEntry(null); } - public PwEntry createEntry(@Nullable PwGroup parent) { + public PwEntryInterface createEntry(@Nullable PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -540,8 +540,8 @@ public class Database { return null; } - public PwGroup createGroup(PwGroup parent) { - PwGroup newPwGroup = null; + public PwGroupInterface createGroup(PwGroupInterface parent) { + PwGroupInterface newPwGroup = null; try { switch (getPwDatabase().getVersion()) { case V3: @@ -556,22 +556,15 @@ public class Database { return newPwGroup; } - public void addEntryTo(PwEntry entry, PwGroup parent) { - try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).addEntryTo((PwEntryV3) entry, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).addEntryTo((PwEntryV4) entry, (PwGroupV4) parent); - break; - } - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be added in this version of PwGroup", e); - } + public void addEntryTo(PwEntryV3 entry, PwGroupV3 parent) { + ((PwDatabaseV3) getPwDatabase()).addEntryTo(entry, parent); } - public void removeEntryFrom(PwEntry entry, PwGroup parent) { + public void addEntryTo(PwEntryInterface entry, PwGroupInterface parent) { + ((PwDatabaseV4) getPwDatabase()).addEntryTo(entry, parent); + } + + public void removeEntryFrom(PwEntryInterface entry, PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -586,7 +579,7 @@ public class Database { } } - public void addGroupTo(PwGroup group, PwGroup parent) { + public void addGroupTo(PwGroupInterface group, PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -601,7 +594,7 @@ public class Database { } } - public void removeGroupFrom(PwGroup group, PwGroup parent) { + public void removeGroupFrom(PwGroupInterface group, PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -616,7 +609,7 @@ public class Database { } } - public boolean canRecycle(PwEntry entry) { + public boolean canRecycle(PwEntryInterface entry) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -630,7 +623,7 @@ public class Database { return false; } - public boolean canRecycle(PwGroup group) { + public boolean canRecycle(PwGroupInterface group) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -644,7 +637,7 @@ public class Database { return false; } - public void recycle(PwEntry entry) { + public void recycle(PwEntryInterface entry) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -659,7 +652,7 @@ public class Database { } } - public void recycle(PwGroup group) { + public void recycle(PwGroupInterface group) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -674,7 +667,7 @@ public class Database { } } - public void updateEntry(PwEntry oldEntry, PwEntry newEntry) { + public void updateEntry(PwEntryInterface oldEntry, PwEntryInterface newEntry) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -689,7 +682,7 @@ public class Database { } } - public void updateGroup(PwGroup oldGroup, PwGroup newGroup) { + public void updateGroup(PwGroupInterface oldGroup, PwGroupInterface newGroup) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -709,7 +702,7 @@ public class Database { * @param entryToCopy * @param newParent */ - public @Nullable PwEntry copyEntry(PwEntry entryToCopy, PwGroup newParent) { + public @Nullable PwEntryInterface copyEntry(PwEntryInterface entryToCopy, PwGroupInterface newParent) { try { // TODO encapsulate switch (getPwDatabase().getVersion()) { @@ -732,17 +725,17 @@ public class Database { return null; } - public void moveEntry(PwEntry entryToMove, PwGroup newParent) { - removeEntryFrom(entryToMove, entryToMove.parent); + public void moveEntry(PwEntryInterface entryToMove, PwGroupInterface newParent) { + removeEntryFrom(entryToMove, entryToMove.getParent()); addEntryTo(entryToMove, newParent); } - public void moveGroup(PwGroup groupToMove, PwGroup newParent) { - removeGroupFrom(groupToMove, groupToMove.parent); + public void moveGroup(PwGroupInterface groupToMove, PwGroupInterface newParent) { + removeGroupFrom(groupToMove, groupToMove.getParent()); addGroupTo(groupToMove, newParent); } - public void deleteEntry(PwEntry entry) { + public void deleteEntry(PwEntryInterface entry) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -757,7 +750,7 @@ public class Database { } } - public void deleteGroup(PwGroup group) { + public void deleteGroup(PwGroupInterface group) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -780,7 +773,7 @@ public class Database { return getPwDatabase().isRecycleBinEnabled(); } - public void undoRecycle(PwEntry entry, PwGroup parent) { + public void undoRecycle(PwEntryInterface entry, PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -795,7 +788,7 @@ public class Database { } } - public void undoRecycle(PwGroup group, PwGroup parent) { + public void undoRecycle(PwGroupInterface group, PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -810,7 +803,7 @@ public class Database { } } - public void undoDeleteEntry(PwEntry entry, PwGroup parent) { + public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: @@ -825,7 +818,7 @@ public class Database { } } - public void undoDeleteGroup(PwGroup group, PwGroup parent) { + public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { case V3: diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index b576173d0..2ab7127e0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -35,8 +35,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; -public abstract class PwDatabase, - PwEntryDB extends PwEntry> { +public abstract class PwDatabase { public static final UUID UUID_ZERO = new UUID(0,0); @@ -46,11 +45,11 @@ public abstract class PwDatabase protected byte masterKey[] = new byte[32]; protected byte[] finalKey; - protected PwGroupDB rootGroup; + protected PwGroupInterface rootGroup; protected PwIconFactory iconFactory = new PwIconFactory(); - protected Map groups = new HashMap<>(); - protected Map entries = new HashMap<>(); + protected Map groups = new HashMap<>(); + protected Map entries = new HashMap<>(); private static boolean isKDBExtension(String filename) { if (filename == null) { return false; } @@ -72,11 +71,11 @@ public abstract class PwDatabase public abstract PwVersion getVersion(); - public PwGroupDB getRootGroup() { + public PwGroupInterface getRootGroup() { return rootGroup; } - public void setRootGroup(PwGroupDB rootGroup) { + public void setRootGroup(PwGroupInterface rootGroup) { this.rootGroup = rootGroup; } @@ -248,11 +247,11 @@ public abstract class PwDatabase public abstract List getAvailableEncryptionAlgorithms(); - public abstract List getGrpRoots(); + public abstract List getGrpRoots(); - public abstract List getGroups(); + public abstract List getGroups(); - protected void addGroupTo(PwGroupDB newGroup, PwGroupDB parent) { + protected void addGroupTo(PwGroupInterface newGroup, PwGroupInterface parent) { // Add tree to parent tree if ( parent == null ) { parent = rootGroup; @@ -265,7 +264,7 @@ public abstract class PwDatabase parent.touch(true, true); } - protected void removeGroupFrom(PwGroupDB remove, PwGroupDB parent) { + protected void removeGroupFrom(PwGroupInterface remove, PwGroupInterface parent) { // Remove tree from parent tree if (parent != null) { parent.removeChildGroup(remove); @@ -273,9 +272,9 @@ public abstract class PwDatabase groups.remove(remove.getId()); } - public abstract PwGroupId newGroupId(); + public abstract PwNodeId newGroupId(); - public PwGroupDB getGroupByGroupId(PwGroupId id) { + public PwGroupInterface getGroupByGroupId(PwNodeId id) { return this.groups.get(id); } @@ -286,11 +285,11 @@ public abstract class PwDatabase * ID number to check for * @return True if the ID is used, false otherwise */ - protected boolean isGroupIdUsed(PwGroupId id) { - List groups = getGroups(); + protected boolean isGroupIdUsed(PwNodeId id) { + List groups = getGroups(); for (int i = 0; i < groups.size(); i++) { - PwGroupDB group =groups.get(i); + PwGroupInterface group =groups.get(i); if (group.getId().equals(id)) { return true; } @@ -299,15 +298,15 @@ public abstract class PwDatabase return false; } - public abstract PwGroupDB createGroup(); + public abstract PwGroupInterface createGroup(); - public abstract List getEntries(); + public abstract List getEntries(); - public PwEntryDB getEntryByUUIDId(UUID id) { + public PwEntryInterface getEntryByUUIDId(UUID id) { return this.entries.get(id); } - protected void addEntryTo(PwEntryDB newEntry, PwGroupDB parent) { + protected void addEntryTo(PwEntryInterface newEntry, PwGroupInterface parent) { // Add entry to parent if (parent != null) { parent.addChildEntry(newEntry); @@ -317,7 +316,7 @@ public abstract class PwDatabase entries.put(newEntry.getUUID(), newEntry); } - protected void removeEntryFrom(PwEntryDB remove, PwGroupDB parent) { + protected void removeEntryFrom(PwEntryInterface remove, PwGroupInterface parent) { // Remove entry for parent if (parent != null) { parent.removeChildEntry(remove); @@ -325,20 +324,20 @@ public abstract class PwDatabase entries.remove(remove.getUUID()); } - public abstract boolean isBackup(PwGroupDB group); + public abstract boolean isBackup(PwGroupInterface group); - protected void populateGlobals(PwGroupDB currentGroup) { + protected void populateGlobals(PwGroupInterface currentGroup) { - List childGroups = currentGroup.getChildGroups(); - List childEntries = currentGroup.getChildEntries(); + List childGroups = currentGroup.getChildGroups(); + List childEntries = currentGroup.getChildEntries(); for (int i = 0; i < childEntries.size(); i++ ) { - PwEntryDB cur = childEntries.get(i); + PwEntryInterface cur = childEntries.get(i); entries.put(cur.getUUID(), cur); } for (int i = 0; i < childGroups.size(); i++ ) { - PwGroupDB cur = childGroups.get(i); + PwGroupInterface cur = childGroups.get(i); groups.put(cur.getId(), cur); populateGlobals(cur); } @@ -365,7 +364,7 @@ public abstract class PwDatabase * @param group Group to remove * @return true if group can be recycle, false elsewhere */ - protected boolean canRecycle(PwGroupDB group) { + protected boolean canRecycle(PwGroupInterface group) { return false; } @@ -374,54 +373,54 @@ public abstract class PwDatabase * @param entry Entry to remove * @return true if entry can be recycle, false elsewhere */ - protected boolean canRecycle(PwEntryDB entry) { + protected boolean canRecycle(PwEntryInterface entry) { return false; } - protected void recycle(PwGroupDB group) { + protected void recycle(PwGroupInterface group) { // Assume calls to this are protected by calling inRecyleBin throw new RuntimeException("Call not valid for .kdb databases."); } - protected void recycle(PwEntryDB entry) { + protected void recycle(PwEntryInterface entry) { // Assume calls to this are protected by calling inRecyleBin throw new RuntimeException("Call not valid for .kdb databases."); } - protected void undoRecycle(PwGroupDB group, PwGroupDB origParent) { + protected void undoRecycle(PwGroupInterface group, PwGroupInterface origParent) { throw new RuntimeException("Call not valid for .kdb databases."); } - protected void undoRecycle(PwEntryDB entry, PwGroupDB origParent) { + protected void undoRecycle(PwEntryInterface entry, PwGroupInterface origParent) { throw new RuntimeException("Call not valid for .kdb databases."); } - protected void deleteGroup(PwGroupDB group) { - PwGroupDB parent = group.getParent(); // TODO inference + protected void deleteGroup(PwGroupInterface group) { + PwGroupInterface parent = group.getParent(); // TODO inference removeGroupFrom(group, parent); parent.touch(false, true); } - protected void deleteEntry(PwEntryDB entry) { - PwGroupDB parent = entry.getParent(); // TODO inference + protected void deleteEntry(PwEntryInterface entry) { + PwGroupInterface parent = entry.getParent(); // TODO inference removeEntryFrom(entry, parent); parent.touch(false, true); } // TODO Delete group - public void undoDeleteGroup(PwGroupDB group, PwGroupDB origParent) { + public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface origParent) { addGroupTo(group, origParent); } - public void undoDeleteEntry(PwEntryDB entry, PwGroupDB origParent) { + public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface origParent) { addEntryTo(entry, origParent); } - public PwGroupDB getRecycleBin() { + public PwGroupInterface getRecycleBin() { return null; } - public boolean isGroupSearchable(PwGroupDB group, boolean omitBackup) { + public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { return group != null; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index 4c950c0df..f7c9f0ae4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -64,14 +64,14 @@ import java.util.Random; * @author Bill Zwicky * @author Dominik Reichl */ -public class PwDatabaseV3 extends PwDatabase { +public class PwDatabaseV3 extends PwDatabase { private static final int DEFAULT_ENCRYPTION_ROUNDS = 300; // all entries - private List entries = new ArrayList<>(); + private List entries = new ArrayList<>(); // all groups - private List groups = new ArrayList<>(); + private List groups = new ArrayList<>(); private int numKeyEncRounds; @@ -87,10 +87,10 @@ public class PwDatabaseV3 extends PwDatabase { initAndAddGroup("eMail", 19, rootGroup); } - private void initAndAddGroup(String name, int iconId, PwGroupV3 parent) { + private void initAndAddGroup(String title, int iconId, PwGroupInterface parent) { PwGroupV3 group = createGroup(); group.setId(newGroupId()); - group.setName(name); + group.setName(title); group.setIconStandard(iconFactory.getIcon(iconId)); addGroupTo(group, parent); } @@ -108,11 +108,11 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public List getGroups() { + public List getGroups() { return groups; } - public void setGroups(List grp) { + public void setGroups(List grp) { groups = grp; } @@ -125,11 +125,11 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public List getEntries() { + public List getEntries() { return entries; } - public PwEntry getEntryAt(int position) { + public PwEntryInterface getEntryAt(int position) { return entries.get(position); } @@ -142,23 +142,23 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public List getGrpRoots() { + public List getGrpRoots() { int target = 0; - List kids = new ArrayList<>(); + List kids = new ArrayList<>(); for (int i = 0; i < groups.size(); i++) { - PwGroupV3 grp = groups.get(i); + PwGroupInterface grp = groups.get(i); if (grp.getLevel() == target) kids.add(grp); } return kids; } - public List getGrpChildren(PwGroupV3 parent) { + public List getGrpChildren(PwGroupInterface parent) { int idx = groups.indexOf(parent); int target = parent.getLevel() + 1; - List kids = new ArrayList<>(); + List kids = new ArrayList<>(); while (++idx < groups.size()) { - PwGroupV3 grp = groups.get(idx); + PwGroupInterface grp = groups.get(idx); if (grp.getLevel() < target) break; else if (grp.getLevel() == target) @@ -167,33 +167,33 @@ public class PwDatabaseV3 extends PwDatabase { return kids; } - public List getEntries(PwGroupV3 parent) { - List kids = new ArrayList<>(); + public List getEntries(PwGroupInterface parent) { + List kids = new ArrayList<>(); /* * for( Iterator i = entries.iterator(); i.hasNext(); ) { PwEntryV3 ent * = (PwEntryV3)i.next(); if( ent.groupId == parent.groupId ) kids.add( * ent ); } */ for (int i = 0; i < entries.size(); i++) { - PwEntryV3 ent = entries.get(i); + PwEntryInterface ent = entries.get(i); if (ent.getParent().getGroupId() == parent.getGroupId()) kids.add(ent); } return kids; } - public void constructTree(PwGroupV3 currentGroup) { + public void constructTree(PwGroupInterface currentGroup) { // I'm in root if (currentGroup == null) { PwGroupV3 root = new PwGroupV3(); rootGroup = root; - List rootChildGroups = getGrpRoots(); + List rootChildGroups = getGrpRoots(); root.setGroups(rootChildGroups); root.setEntries(new ArrayList<>()); root.setLevel(-1); for (int i = 0; i < rootChildGroups.size(); i++) { - PwGroupV3 grp = rootChildGroups.get(i); + PwGroupInterface grp = rootChildGroups.get(i); grp.setParent(root); constructTree(grp); } @@ -207,12 +207,12 @@ public class PwDatabaseV3 extends PwDatabase { // set parent in child entries for (int i = 0; i < currentGroup.numbersOfChildEntries(); i++) { - PwEntryV3 entry = currentGroup.getChildEntryAt(i); + PwEntryInterface entry = currentGroup.getChildEntryAt(i); entry.setParent(currentGroup); } // recursively construct child groups for (int i = 0; i < currentGroup.numbersOfChildGroups(); i++) { - PwGroupV3 grp = currentGroup.getChildGroupAt(i); + PwGroupInterface grp = currentGroup.getChildGroupAt(i); grp.setParent(currentGroup); constructTree(currentGroup.getChildGroupAt(i)); } @@ -231,11 +231,11 @@ public class PwDatabaseV3 extends PwDatabase { * @return new tree id */ @Override - public PwGroupIdV3 newGroupId() { - PwGroupIdV3 newId; + public PwNodeIdInt newGroupId() { + PwNodeIdInt newId; Random random = new Random(); while (true) { - newId = new PwGroupIdV3(random.nextInt()); + newId = new PwNodeIdInt(random.nextInt()); if (!isGroupIdUsed(newId)) break; } @@ -311,7 +311,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public void addEntryTo(PwEntryV3 newEntry, PwGroupV3 parent) { + public void addEntryTo(PwEntryInterface newEntry, PwGroupInterface parent) { super.addEntryTo(newEntry, parent); // Add entry to root entries @@ -319,7 +319,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public void addGroupTo(PwGroupV3 newGroup, PwGroupV3 parent) { + public void addGroupTo(PwGroupInterface newGroup, PwGroupInterface parent) { super.addGroupTo(newGroup, parent); // Add tree to root groups @@ -327,7 +327,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public void removeEntryFrom(PwEntryV3 remove, PwGroupV3 parent) { + public void removeEntryFrom(PwEntryInterface remove, PwGroupInterface parent) { super.removeEntryFrom(remove, parent); // Remove entry from root entry @@ -335,7 +335,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public void removeGroupFrom(PwGroupV3 remove, PwGroupV3 parent) { + public void removeGroupFrom(PwGroupInterface remove, PwGroupInterface parent) { super.removeGroupFrom(remove, parent); // Remove tree from root entry @@ -357,7 +357,7 @@ public class PwDatabaseV3 extends PwDatabase { // No-op } @Override - public boolean isBackup(PwGroupV3 group) { + public boolean isBackup(PwGroupInterface group) { while (group != null) { if (group.getLevel() == 0 && group.getName().equalsIgnoreCase("Backup")) { return true; @@ -369,7 +369,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public boolean isGroupSearchable(PwGroupV3 group, boolean omitBackup) { + public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { if (!super.isGroupSearchable(group, omitBackup)) { return false; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index ad72e058f..822489f94 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -61,7 +61,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import biz.source_code.base64Coder.Base64Coder; -public class PwDatabaseV4 extends PwDatabase { +public class PwDatabaseV4 extends PwDatabase { private static final String TAG = PwDatabaseV4.class.getName(); private static final int DEFAULT_HISTORY_MAX_ITEMS = 10; // -1 unlimited @@ -485,51 +485,51 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public List getGroups() { - List list = new ArrayList<>(); - PwGroupV4 root = rootGroup; + public List getGroups() { + List list = new ArrayList<>(); + PwGroupInterface root = rootGroup; buildChildGroupsRecursive(root, list); return list; } - private static void buildChildGroupsRecursive(PwGroupV4 root, List list) { + private static void buildChildGroupsRecursive(PwGroupInterface root, List list) { list.add(root); for ( int i = 0; i < root.numbersOfChildGroups(); i++) { - PwGroupV4 child = root.getChildGroupAt(i); + PwGroupInterface child = root.getChildGroupAt(i); buildChildGroupsRecursive(child, list); } } @Override - public List getGrpRoots() { + public List getGrpRoots() { return rootGroup.getChildGroups(); } @Override - public List getEntries() { - List list = new ArrayList<>(); - PwGroupV4 root = rootGroup; + public List getEntries() { + List list = new ArrayList<>(); + PwGroupInterface root = rootGroup; buildChildEntriesRecursive(root, list); return list; } - private static void buildChildEntriesRecursive(PwGroupV4 root, List list) { + private static void buildChildEntriesRecursive(PwGroupInterface root, List list) { for ( int i = 0; i < root.numbersOfChildEntries(); i++ ) { list.add(root.getChildEntryAt(i)); } for ( int i = 0; i < root.numbersOfChildGroups(); i++ ) { - PwGroupV4 child = root.getChildGroupAt(i); + PwGroupInterface child = root.getChildGroupAt(i); buildChildEntriesRecursive(child, list); } } @Override - public PwGroupIdV4 newGroupId() { - PwGroupIdV4 id; + public PwNodeIdUUID newGroupId() { + PwNodeIdUUID id; while (true) { - id = new PwGroupIdV4(UUID.randomUUID()); + id = new PwNodeIdUUID(UUID.randomUUID()); if (!isGroupIdUsed(id)) break; } @@ -543,7 +543,7 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public boolean isBackup(PwGroupV4 group) { + public boolean isBackup(PwGroupInterface group) { if (!recycleBinEnabled) { return false; } @@ -552,7 +552,7 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public void populateGlobals(PwGroupV4 currentGroup) { + public void populateGlobals(PwGroupInterface currentGroup) { groups.put(rootGroup.getId(), rootGroup); super.populateGlobals(currentGroup); @@ -608,32 +608,32 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public boolean canRecycle(PwGroupV4 group) { + public boolean canRecycle(PwGroupInterface group) { if (!recycleBinEnabled) { return false; } - PwGroup recycle = getRecycleBin(); + PwGroupInterface recycle = getRecycleBin(); return (recycle == null) || (!group.isContainedIn(recycle)); } @Override - public boolean canRecycle(PwEntryV4 entry) { + public boolean canRecycle(PwEntryInterface entry) { if (!recycleBinEnabled) { return false; } - PwGroupV4 parent = entry.getParent(); + PwGroupInterface parent = entry.getParent(); return (parent != null) && canRecycle(parent); } @Override - public void recycle(PwGroupV4 group) { + public void recycle(PwGroupInterface group) { ensureRecycleBin(); - PwGroupV4 parent = group.getParent(); + PwGroupInterface parent = group.getParent(); removeGroupFrom(group, parent); parent.touch(false, true); - PwGroupV4 recycleBin = getRecycleBin(); + PwGroupInterface recycleBin = getRecycleBin(); addGroupTo(group, recycleBin); group.touch(false, true); @@ -641,14 +641,14 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public void recycle(PwEntryV4 entry) { + public void recycle(PwEntryInterface entry) { ensureRecycleBin(); - - PwGroupV4 parent = entry.getParent(); + + PwGroupInterface parent = entry.getParent(); removeEntryFrom(entry, parent); parent.touch(false, true); - - PwGroupV4 recycleBin = getRecycleBin(); + + PwGroupInterface recycleBin = getRecycleBin(); addEntryTo(entry, recycleBin); entry.touch(false, true); @@ -656,18 +656,18 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public void undoRecycle(PwGroupV4 group, PwGroupV4 origParent) { + public void undoRecycle(PwGroupInterface group, PwGroupInterface origParent) { - PwGroupV4 recycleBin = getRecycleBin(); + PwGroupInterface recycleBin = getRecycleBin(); removeGroupFrom(group, recycleBin); addGroupTo(group, origParent); } @Override - public void undoRecycle(PwEntryV4 entry, PwGroupV4 origParent) { - - PwGroupV4 recycleBin = getRecycleBin(); + public void undoRecycle(PwEntryInterface entry, PwGroupInterface origParent) { + + PwGroupInterface recycleBin = getRecycleBin(); removeEntryFrom(entry, recycleBin); addEntryTo(entry, origParent); @@ -682,24 +682,24 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public void deleteEntry(PwEntryV4 entry) { + public void deleteEntry(PwEntryInterface entry) { super.deleteEntry(entry); deletedObjects.add(new PwDeletedObject(entry.getUUID())); } @Override - public void undoDeleteEntry(PwEntryV4 entry, PwGroupV4 origParent) { + public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface origParent) { super.undoDeleteEntry(entry, origParent); deletedObjects.remove(new PwDeletedObject(entry.getUUID())); } @Override - public PwGroupV4 getRecycleBin() { // TODO delete recycle bin preference + public PwGroupInterface getRecycleBin() { // TODO delete recycle bin preference if (recycleBinUUID == null) { return null; } - PwGroupId recycleId = new PwGroupIdV4(recycleBinUUID); + PwNodeId recycleId = new PwNodeIdUUID(recycleBinUUID); return groups.get(recycleId); } @@ -724,7 +724,7 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public boolean isGroupSearchable(PwGroupV4 group, boolean omitBackup) { + public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { if (!super.isGroupSearchable(group, omitBackup)) { return false; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.java deleted file mode 100644 index 6c3084669..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -import com.kunzisoft.keepass.database.ExtraFields; -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; -import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.model.Entry; -import com.kunzisoft.keepass.model.Field; - -import java.util.UUID; - -public abstract class PwEntry extends PwNode { - - private static final String PMS_TAN_ENTRY = ""; - - protected UUID uuid = PwDatabase.UUID_ZERO; - - public PwEntry() {} - - public PwEntry(Parcel in) { - super(in); - uuid = (UUID) in.readSerializable(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeSerializable(uuid); - } - - @Override - protected void construct(Parent parent) { - super.construct(parent); - uuid = UUID.randomUUID(); - } - - @Override - public PwEntry clone() { - // uuid is clone automatically (IMMUTABLE) - return (PwEntry) super.clone(); - } - - @Override - public Type getType() { - return Type.ENTRY; - } - - protected void assign(PwEntry source) { - super.assign(source); - uuid = source.uuid; - } - - public UUID getUUID() { - return uuid; - } - - public void setUUID(UUID uuid) { - this.uuid = uuid; - } - - public void startToManageFieldReferences(PwDatabase db) {} - public void stopToManageFieldReferences() {} - - public abstract String getTitle(); - public abstract void setTitle(String title); - - public abstract String getUsername(); - public abstract void setUsername(String user); - - public abstract String getPassword(); - public abstract void setPassword(String pass); - - public abstract String getUrl(); - public abstract void setUrl(String url); - - public abstract String getNotes(); - public abstract void setNotes(String notes); - - private boolean isTan() { - return getTitle().equals(PMS_TAN_ENTRY) && (getUsername().length() > 0); - } - - /** - * {@inheritDoc} - * Get the display title from an entry,
- * {@link #startToManageFieldReferences(PwDatabase)} and {@link #stopToManageFieldReferences()} must be called - * before and after {@link #getVisualTitle()} - */ - public String getVisualTitle() { - // only used to compare, don't car if it's a reference - return getVisualTitle(isTan(), getTitle(), getUsername(), getUrl(), getUUID()); - } - - public static String getVisualTitle(boolean isTAN, String title, String username, String url, UUID uuid) { - if ( isTAN ) { - return PMS_TAN_ENTRY + " " + username; - } else { - if (title.isEmpty()) - if (url.isEmpty()) - return uuid.toString(); - else - return url; - else - return title; - } - } - - // TODO encapsulate extra fields - - /** - * To redefine if version of entry allow extra field, - * @return true if entry allows extra field - */ - public boolean allowExtraFields() { - return false; - } - - /** - * Retrieve extra fields to show, key is the label, value is the value of field - * @return Map of label/value - */ - public ExtraFields getFields() { - return new ExtraFields(); - } - - /** - * If entry contains extra fields - * @return true if there is extra fields - */ - public boolean containsCustomFields() { - return getFields().containsCustomFields(); - } - - /** - * If entry contains extra fields that are protected - * @return true if there is extra fields protected - */ - public boolean containsCustomFieldsProtected() { - return getFields().containsCustomFieldsProtected(); - } - - /** - * If entry contains extra fields that are not protected - * @return true if there is extra fields not protected - */ - public boolean containsCustomFieldsNotProtected() { - return getFields().containsCustomFieldsNotProtected(); - } - - /** - * Add an extra field to the list (standard or custom) - * @param label Label of field, must be unique - * @param value Value of field - */ - public void addExtraField(String label, ProtectedString value) {} - - /** - * Delete all custom fields - */ - public void removeAllCustomFields() {} - - /** - * If it's a node with only meta information like Meta-info SYSTEM Database Color - * @return false by default, true if it's a meta stream - */ - public boolean isMetaStream() { - return false; - } - - /** - * Create a backup of entry - */ - public void createBackup(PwDatabase db) {} - - public EntrySearchStringIterator stringIterator() { - return EntrySearchStringIterator.getInstance(this); - } - - public void touchLocation() { } - - public boolean isSearchingEnabled() { - return false; - } - - public Entry getEntry() { - Entry entryModel = new Entry(); - entryModel.setTitle(getTitle()); - entryModel.setUsername(getUsername()); - entryModel.setPassword(getPassword()); - entryModel.setUrl(getUrl()); - if (containsCustomFields()) { - getFields() - .doActionToAllCustomProtectedField( - (key, value) -> entryModel.addCustomField( - new Field(key, value.toString()))); - } - return entryModel; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - PwEntry pwEntry = (PwEntry) o; - return isSameType(pwEntry) - && (getUUID() != null ? getUUID().equals(pwEntry.getUUID()) : pwEntry.getUUID() == null); - } - - @Override - public int hashCode() { - return getUUID() != null ? getUUID().hashCode() : 0; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java new file mode 100644 index 000000000..49f74156e --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java @@ -0,0 +1,140 @@ +package com.kunzisoft.keepass.database.element; + +import com.kunzisoft.keepass.database.ExtraFields; +import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.model.Entry; +import com.kunzisoft.keepass.model.Field; + +import java.util.UUID; + +public interface PwEntryInterface extends PwNodeInterface { + + UUID getUUID(); + + void setUUID(UUID uuid); + + String getName(); + void setName(String title); + + String getUsername(); + void setUsername(String user); + + String getPassword(); + void setPassword(String pass); + + String getUrl(); + void setUrl(String url); + + String getNotes(); + void setNotes(String notes); + + + /** + * To redefine if version of entry allow extra field, + * @return true if entry allows extra field + */ + boolean allowExtraFields(); + + /** + * Retrieve extra fields to show, key is the label, value is the value of field + * @return Map of label/value + */ + ExtraFields getFields(); + + /** + * If entry contains extra fields + * @return true if there is extra fields + */ + boolean containsCustomFields(); + /** + * If entry contains extra fields that are protected + * @return true if there is extra fields protected + */ + boolean containsCustomFieldsProtected(); + + /** + * If entry contains extra fields that are not protected + * @return true if there is extra fields not protected + */ + boolean containsCustomFieldsNotProtected(); + + /** + * Add an extra field to the list (standard or custom) + * @param label Label of field, must be unique + * @param value Value of field + */ + void addExtraField(String label, ProtectedString value); + + /** + * Delete all custom fields + */ + void removeAllCustomFields(); + + /** + * If it's a node with only meta information like Meta-info SYSTEM Database Color + * @return false by default, true if it's a meta stream + */ + boolean isMetaStream(); + + /** + * Create a backup of entry + */ + void createBackup(PwDatabase db); + + void touchLocation(); + + void startToManageFieldReferences(PwDatabase db); + void stopToManageFieldReferences(); + + PwEntryInterface duplicate(); + + String PMS_TAN_ENTRY = ""; + + static boolean isTan(PwEntryInterface entry) { + return entry.getName().equals(PMS_TAN_ENTRY) + && (entry.getUsername().length() > 0); + } + + /** + * {@inheritDoc} + * Get the display title from an entry,
+ * {@link #startToManageFieldReferences(PwDatabase)} and {@link #stopToManageFieldReferences()} must be called + * before and after {@link #getVisualTitle(PwEntryInterface entry)} + */ + static String getVisualTitle(boolean isTan, String userName, String title, String url, UUID uuid) { + if ( isTan ) { + return PMS_TAN_ENTRY + " " + userName; + } else { + if (title.isEmpty()) + if (url.isEmpty()) + return uuid.toString(); + else + return url; + else + return title; + } + } + + static String getVisualTitle(PwEntryInterface entry) { + return getVisualTitle(PwEntryInterface.isTan(entry), + entry.getUsername(), + entry.getName(), + entry.getUrl(), + entry.getUUID()); + } + + static Entry getEntry(PwEntryInterface entry) { + Entry entryModel = new Entry(); + entryModel.setTitle(entry.getName()); + entryModel.setUsername(entry.getUsername()); + entryModel.setPassword(entry.getPassword()); + entryModel.setUrl(entry.getUrl()); + if (entry.containsCustomFields()) { + entry.getFields() + .doActionToAllCustomProtectedField( + (key, value) -> entryModel.addCustomField( + new Field(key, value.toString()))); + } + return entryModel; + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index 9c4c7ba88..da5b84117 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -44,6 +44,9 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import com.kunzisoft.keepass.database.ExtraFields; +import com.kunzisoft.keepass.database.security.ProtectedString; + import java.io.UnsupportedEncodingException; import java.util.UUID; @@ -69,7 +72,7 @@ import java.util.UUID; * @author Dominik Reichl * @author Jeremy Jamet */ -public class PwEntryV3 extends PwEntry { +public class PwEntryV3 extends PwNode implements PwEntryInterface { /** Size of byte buffer needed to hold this struct. */ private static final String PMS_ID_BINDESC = "bin-stream"; @@ -83,26 +86,26 @@ public class PwEntryV3 extends PwEntry { private String url; private String additional; /** A string describing what is in pBinaryData */ - private String binaryDesc; - private byte[] binaryData; + private String binaryDesc; + private byte[] binaryData; public PwEntryV3() { super(); } - public PwEntryV3(PwGroupV3 p) { - construct(p); + public PwEntryV3(PwGroupV3 parent) { + super(parent); } - public PwEntryV3(Parcel in) { - super(in); - title = in.readString(); - username = in.readString(); - in.readByteArray(password); - url = in.readString(); - additional = in.readString(); - binaryDesc = in.readString(); - in.readByteArray(binaryData); + public PwEntryV3(Parcel parcel) { + super(parcel); + title = parcel.readString(); + username = parcel.readString(); + parcel.readByteArray(password); + url = parcel.readString(); + additional = parcel.readString(); + binaryDesc = parcel.readString(); + parcel.readByteArray(binaryData); } @Override @@ -131,7 +134,6 @@ public class PwEntryV3 extends PwEntry { protected void updateWith(PwEntryV3 source) { super.assign(source); - title = source.title; username = source.username; int passLen = source.password.length; @@ -176,9 +178,20 @@ public class PwEntryV3 extends PwEntry { return newEntry; } + @Override + public PwEntryInterface duplicate() { + return clone(); + } + + @Override + public Type getType() { + return Type.ENTRY; + } + public void setGroupId(int groupId) { - this.parent = new PwGroupV3(); - this.parent.setGroupId(groupId); + PwGroupV3 parentGroup = new PwGroupV3(); + parentGroup.setGroupId(groupId); + this.parent = parentGroup; } @Override @@ -195,12 +208,12 @@ public class PwEntryV3 extends PwEntry { } @Override - public String getTitle() { + public String getName() { return title; } - @Override - public void setTitle(String title) { + @Override + public void setName(String title) { this.title = title; } @@ -350,4 +363,52 @@ public class PwEntryV3 extends PwEntry { return true; } + + @Override + public void createBackup(PwDatabase db) {} + + @Override + public boolean isSearchingEnabled() { + return false; + } + + @Override + public void startToManageFieldReferences(PwDatabase db) {} + + @Override + public void stopToManageFieldReferences() {} + + @Override + public void touchLocation() {} + + @Override + public boolean allowExtraFields() { + return false; + } + + @Override + public ExtraFields getFields() { + return new ExtraFields(); // TODO Nullable + } + + @Override + public boolean containsCustomFields() { + return getFields().containsCustomFields(); + } + + @Override + public boolean containsCustomFieldsProtected() { + return getFields().containsCustomFieldsProtected(); + } + + @Override + public boolean containsCustomFieldsNotProtected() { + return getFields().containsCustomFieldsNotProtected(); + } + + @Override + public void addExtraField(String label, ProtectedString value) {} + + @Override + public void removeAllCustomFields() {} } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 06eebc71a..964407cd8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -35,7 +35,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -public class PwEntryV4 extends PwEntry implements ITimeLogger { +public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { public static final String STR_TITLE = "Title"; public static final String STR_USERNAME = "UserName"; @@ -66,8 +66,8 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { super(); } - public PwEntryV4(PwGroupV4 p) { - construct(p); + public PwEntryV4(PwGroupV4 parent) { + super(parent); } public void updateWith(PwEntryV4 source) { @@ -89,22 +89,22 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { tags = source.tags; } - public PwEntryV4(Parcel in) { - super(in); - customIcon = in.readParcelable(PwIconCustom.class.getClassLoader()); - usageCount = in.readLong(); - parentGroupLastMod = in.readParcelable(PwDate.class.getClassLoader()); - customData = MemUtil.readStringParcelableMap(in); - fields = in.readParcelable(ExtraFields.class.getClassLoader()); - binaries = MemUtil.readStringParcelableMap(in, ProtectedBinary.class); - foregroundColor = in.readString(); - backgroupColor = in.readString(); - overrideURL = in.readString(); - autoType = in.readParcelable(AutoType.class.getClassLoader()); - history = in.readArrayList(PwEntryV4.class.getClassLoader()); // TODO verify - url = in.readString(); - additional = in.readString(); - tags = in.readString(); + public PwEntryV4(Parcel parcel) { + super(parcel); + customIcon = parcel.readParcelable(PwIconCustom.class.getClassLoader()); + usageCount = parcel.readLong(); + parentGroupLastMod = parcel.readParcelable(PwDate.class.getClassLoader()); + customData = MemUtil.readStringParcelableMap(parcel); + fields = parcel.readParcelable(ExtraFields.class.getClassLoader()); + binaries = MemUtil.readStringParcelableMap(parcel, ProtectedBinary.class); + foregroundColor = parcel.readString(); + backgroupColor = parcel.readString(); + overrideURL = parcel.readString(); + autoType = parcel.readParcelable(AutoType.class.getClassLoader()); + history = parcel.readArrayList(PwEntryV4.class.getClassLoader()); // TODO verify + url = parcel.readString(); + additional = parcel.readString(); + tags = parcel.readString(); } @Override @@ -165,6 +165,16 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { return newEntry; } + @Override + public PwEntryInterface duplicate() { + return clone(); + } + + @Override + public Type getType() { + return Type.ENTRY; + } + @Override public void startToManageFieldReferences(PwDatabase db) { this.mDatabase = (PwDatabaseV4) db; @@ -176,7 +186,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { this.mDatabase = null; this.mDecodeRef = false; } - + private String decodeRefKey(boolean decodeRef, String key) { String text = getProtectedStringValue(key); if (decodeRef) { @@ -197,17 +207,17 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { } @Override - public String getTitle() { + public String getName() { return decodeRefKey(mDecodeRef, STR_TITLE); } - + @Override public String getPassword() { return decodeRefKey(mDecodeRef, STR_PASSWORD); } @Override - public void setTitle(String title) { + public void setName(String title) { boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectTitle; setProtectedString(STR_TITLE, title, protect); } @@ -301,6 +311,21 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { return fields; } + @Override + public boolean containsCustomFields() { + return getFields().containsCustomFields(); + } + + @Override + public boolean containsCustomFieldsProtected() { + return getFields().containsCustomFieldsProtected(); + } + + @Override + public boolean containsCustomFieldsNotProtected() { + return getFields().containsCustomFieldsNotProtected(); + } + public void addExtraField(String label, ProtectedString value) { fields.putProtectedString(label, value); } @@ -422,8 +447,6 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { @Override public void createBackup(PwDatabase db) { - super.createBackup(db); - PwEntryV4 copy = clone(); copy.history = new ArrayList<>(); history.add(copy); @@ -469,7 +492,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { int index = -1; for (int i = 0; i < history.size(); i++) { - PwEntry entry = history.get(i); + PwEntryV4 entry = history.get(i); Date lastMod = entry.getLastModificationTime().getDate(); if ((min == null) || lastMod.before(min)) { index = i; @@ -499,4 +522,12 @@ public class PwEntryV4 extends PwEntry implements ITimeLogger { } return PwGroupV4.DEFAULT_SEARCHING_ENABLED; } + + /** + * If it's a node with only meta information like Meta-info SYSTEM Database Color + * @return false by default, true if it's a meta stream + */ + public boolean isMetaStream() { + return false; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java deleted file mode 100644 index 4f28a62a3..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; - -import java.util.ArrayList; -import java.util.List; - -public abstract class PwGroup - extends PwNode { - - protected String name = ""; - - // TODO verify children not needed - transient protected List childGroups = new ArrayList<>(); - transient protected List childEntries = new ArrayList<>(); - - protected PwGroup() { - super(); - } - - protected PwGroup(Parcel in) { - super(in); - name = in.readString(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeString(name); - } - - @Override - public PwGroup clone() { - // name is clone automatically (IMMUTABLE) - return (PwGroup) super.clone(); - } - - protected void assign(PwGroup source) { - super.assign(source); - name = source.name; - } - - public List getChildGroups() { - return childGroups; - } - - public List getChildEntries() { - return childEntries; - } - - public void setGroups(List groups) { - childGroups = groups; - } - - public void setEntries(List entries) { - childEntries = entries; - } - - public void addChildGroup(GroupG group) { - this.childGroups.add(group); - } - - public void addChildEntry(EntryE entry) { - this.childEntries.add(entry); - } - - public GroupG getChildGroupAt(int number) { - return this.childGroups.get(number); - } - - public EntryE getChildEntryAt(int number) { - return this.childEntries.get(number); - } - - public void removeChildGroup(GroupG group) { - this.childGroups.remove(group); - } - - public void removeChildEntry(EntryE entry) { - this.childEntries.remove(entry); - } - - public int numbersOfChildGroups() { - return childGroups.size(); - } - - public int numbersOfChildEntries() { - return childEntries.size(); - } - - @Override - public Type getType() { - return Type.GROUP; - } - - /** - * Filter MetaStream entries and return children - * @return List of direct children (one level below) as PwNode - */ - public List getDirectChildren() { - List children = new ArrayList<>(); - children.addAll(childGroups); - for(EntryE child : childEntries) { - if (!child.isMetaStream()) - children.add(child); - } - return children; - } - - public abstract PwGroupId getId(); - public abstract void setId(PwGroupId id); - - @Override - protected String getVisualTitle() { - return getTitle(); - } - - @Override - public String getTitle() { - return getName(); - } - - /** - * The same thing as {@link #getTitle()} - */ - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public boolean allowAddEntryIfIsRoot() { - return false; - } - - public boolean preOrderTraverseTree(GroupHandler groupHandler, - EntryHandler entryHandler) { - if (entryHandler != null) { - for (EntryE entry : childEntries) { - if (!entryHandler.operate(entry)) return false; - } - } - - for (GroupG group : childGroups) { - if ((groupHandler != null) && !groupHandler.operate(group)) return false; - group.preOrderTraverseTree(groupHandler, entryHandler); - } - - return true; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - PwGroup pwGroup = (PwGroup) o; - return isSameType(pwGroup) - && (getId() != null ? getId().equals(pwGroup.getId()) : pwGroup.getId() == null); - } - - @Override - public int hashCode() { - PwGroupId groupId = getId(); - return groupId != null ? groupId.hashCode() : 0; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java new file mode 100644 index 000000000..97427b427 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java @@ -0,0 +1,49 @@ +package com.kunzisoft.keepass.database.element; + +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; + +import java.util.List; + +public interface PwGroupInterface extends PwNodeInterface { + + PwNodeId getId(); + void setId(PwNodeId id); + + List getChildGroups(); + + List getChildEntries(); + + void setGroups(List groups); + + void setEntries(List entries); + + void addChildGroup(PwGroupInterface group); + + void addChildEntry(PwEntryInterface entry); + + PwGroupInterface getChildGroupAt(int number); + + PwEntryInterface getChildEntryAt(int number); + + void removeChildGroup(PwGroupInterface group); + + void removeChildEntry(PwEntryInterface entry); + + int numbersOfChildGroups(); + + int numbersOfChildEntries(); + + /** + * Filter MetaStream entries and return children + * @return List of direct children (one level below) as PwNode + */ + public List getDirectChildren(); + + PwGroupInterface duplicate(); + + public boolean preOrderTraverseTree(GroupHandler groupHandler, + EntryHandler entryHandler); + + public boolean allowAddEntryIfIsRoot(); +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index c1c3af69c..8aa6db0a5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -22,20 +22,36 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -public class PwGroupV3 extends PwGroup { +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; + +import java.util.ArrayList; +import java.util.List; + +public class PwGroupV3 extends PwNode implements PwGroupInterface { + + // TODO verify children not needed + transient private List childGroups = new ArrayList<>(); + transient private List childEntries = new ArrayList<>(); // for tree traversing - private int groupId; + // TODO private int groupId; + private String title = ""; private int level = 0; // short /** Used by KeePass internally, don't use */ private int flags; public PwGroupV3() { - super(); + super(); } + public PwGroupV3(PwGroupV3 parent) { + super(parent); + } + public PwGroupV3(Parcel in) { super(in); + title = in.readString(); groupId = in.readInt(); level = in.readInt(); flags = in.readInt(); @@ -44,6 +60,7 @@ public class PwGroupV3 extends PwGroup { @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); + dest.writeString(title); dest.writeInt(groupId); dest.writeInt(level); dest.writeInt(flags); @@ -61,12 +78,9 @@ public class PwGroupV3 extends PwGroup { } }; - public PwGroupV3(PwGroupV3 p) { - construct(p); - } - protected void updateWith(PwGroupV3 source) { super.assign(source); + title = source.title; groupId = source.groupId; level = source.level; flags = source.flags; @@ -75,19 +89,35 @@ public class PwGroupV3 extends PwGroup { @SuppressWarnings("unchecked") @Override public PwGroupV3 clone() { + // name is clone automatically (IMMUTABLE) // newGroup.groupId stay the same in copy // newGroup.level stay the same in copy // newGroup.flags stay the same in copy return (PwGroupV3) super.clone(); } - @Override - public void setParent(PwGroupV3 parent) { + @Override + public PwGroupInterface duplicate() { + return clone(); + } + + @Override + public Type getType() { + return Type.GROUP; + } + + public void setParent(PwGroupInterface parent) { super.setParent(parent); - level = this.parent.getLevel() + 1; + if (parent instanceof PwGroupV3) // TODO Change + level = ((PwGroupV3) parent).getLevel() + 1; } - public int getGroupId() { + @Override + public boolean isSearchingEnabled() { + return false; + } + + public int getGroupId() { return groupId; } @@ -104,13 +134,13 @@ public class PwGroupV3 extends PwGroup { } @Override - public PwGroupId getId() { - return new PwGroupIdV3(groupId); + public PwNodeId getId() { + return new PwNodeIdInt(groupId); } @Override - public void setId(PwGroupId id) { - PwGroupIdV3 id3 = (PwGroupIdV3) id; + public void setId(PwNodeId id) { + PwNodeIdInt id3 = (PwNodeIdInt) id; groupId = id3.getId(); } @@ -133,8 +163,107 @@ public class PwGroupV3 extends PwGroup { icon = db.getIconFactory().getFolderIcon(); } - if (name == null) { - name = ""; + if (title == null) { + title = ""; } } + + @Override + public String getName() { + return title; + } + + @Override + public void setName(String title) { + this.title = title; + } + + @Override + public List getChildGroups() { + return childGroups; + } + + @Override + public List getChildEntries() { + return childEntries; + } + + @Override + public void setGroups(List groups) { + childGroups = groups; + } + + @Override + public void setEntries(List entries) { + childEntries = entries; + } + + @Override + public void addChildGroup(PwGroupInterface group) { + this.childGroups.add(group); + } + + @Override + public void addChildEntry(PwEntryInterface entry) { + this.childEntries.add(entry); + } + + @Override + public PwGroupInterface getChildGroupAt(int number) { + return this.childGroups.get(number); + } + + @Override + public PwEntryInterface getChildEntryAt(int number) { + return this.childEntries.get(number); + } + + @Override + public void removeChildGroup(PwGroupInterface group) { + this.childGroups.remove(group); + } + + @Override + public void removeChildEntry(PwEntryInterface entry) { + this.childEntries.remove(entry); + } + + @Override + public int numbersOfChildGroups() { + return childGroups.size(); + } + + @Override + public int numbersOfChildEntries() { + return childEntries.size(); + } + + @Override + public List getDirectChildren() { + List children = new ArrayList<>(childGroups); + for(PwEntryInterface child : childEntries) { + if (!child.isMetaStream()) + children.add(child); + } + return children; + } + + public boolean preOrderTraverseTree(GroupHandler groupHandler, + EntryHandler entryHandler) { + if (entryHandler != null) { + for (PwEntryInterface entry : childEntries) { + if (!entryHandler.operate(entry)) return false; + } + } + for (PwGroupInterface group : childGroups) { + if ((groupHandler != null) && !groupHandler.operate(group)) return false; + group.preOrderTraverseTree(groupHandler, entryHandler); + } + return true; + } + + @Override + public boolean allowAddEntryIfIsRoot() { + return false; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 09881be69..e1724ff1c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -21,17 +21,25 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; import com.kunzisoft.keepass.database.ITimeLogger; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; -public class PwGroupV4 extends PwGroup implements ITimeLogger { +public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { public static final boolean DEFAULT_SEARCHING_ENABLED = true; - private UUID uuid = PwDatabase.UUID_ZERO; + // TODO verify children not needed + transient private List childGroups = new ArrayList<>(); + transient private List childEntries = new ArrayList<>(); + + private String title = ""; private PwIconCustom customIcon = PwIconCustom.ZERO; private long usageCount = 0; private PwDate parentGroupLastMod = new PwDate(); @@ -48,20 +56,20 @@ public class PwGroupV4 extends PwGroup implements ITimeLog super(); } - public PwGroupV4(PwGroupV4 p) { - construct(p); + public PwGroupV4(PwGroupV4 parent) { + super(parent); parentGroupLastMod = new PwDate(); } - public PwGroupV4(String name, PwIconStandard icon) { - this.uuid = UUID.randomUUID(); - this.name = name; + public PwGroupV4(String title, PwIconStandard icon) { + super(); + this.title = title; this.icon = icon; } public PwGroupV4(Parcel in) { super(in); - uuid = (UUID) in.readSerializable(); + title = in.readString(); customIcon = in.readParcelable(PwIconCustom.class.getClassLoader()); usageCount = in.readLong(); parentGroupLastMod = in.readParcelable(PwDate.class.getClassLoader()); @@ -80,7 +88,7 @@ public class PwGroupV4 extends PwGroup implements ITimeLog @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeSerializable(uuid); + dest.writeString(title); dest.writeParcelable(customIcon, flags); dest.writeLong(usageCount); dest.writeParcelable(parentGroupLastMod, flags); @@ -108,7 +116,7 @@ public class PwGroupV4 extends PwGroup implements ITimeLog protected void updateWith(PwGroupV4 source) { super.assign(source); - uuid = source.uuid; + title = source.title; customIcon = source.customIcon; usageCount = source.usageCount; parentGroupLastMod = source.parentGroupLastMod; @@ -131,7 +139,7 @@ public class PwGroupV4 extends PwGroup implements ITimeLog PwGroupV4 newGroup = (PwGroupV4) super.clone(); // Attributes here - // newGroup.uuid stay the same in copy + // name is clone automatically (IMMUTABLE) newGroup.customIcon = new PwIconCustom(this.customIcon); // newGroup.usageCount stay the same in copy newGroup.parentGroupLastMod = this.parentGroupLastMod.clone(); @@ -148,6 +156,16 @@ public class PwGroupV4 extends PwGroup implements ITimeLog return newGroup; } + + @Override + public PwGroupInterface duplicate() { + return clone(); + } + + @Override + public Type getType() { + return Type.GROUP; + } public void addGroup(PwGroupV4 subGroup) { if ( subGroup == null ) throw new RuntimeException("subGroup"); @@ -161,22 +179,14 @@ public class PwGroupV4 extends PwGroup implements ITimeLog pe.setParent(this); } - public UUID getUUID() { - return uuid; - } - - public void setUUID(UUID uuid) { - this.uuid = uuid; - } - @Override - public PwGroupId getId() { - return new PwGroupIdV4(uuid); + public PwNodeId getId() { + return new PwNodeIdUUID(uuid); } @Override - public void setId(PwGroupId id) { - PwGroupIdV4 id4 = (PwGroupIdV4) id; + public void setId(PwNodeId id) { + PwNodeIdUUID id4 = (PwNodeIdUUID) id; uuid = id4.getId(); } @@ -300,11 +310,104 @@ public class PwGroupV4 extends PwGroup implements ITimeLog if (search != null) { return search; } - group = group.parent; + group = (PwGroupV4) group.parent; } // If we get to the root tree and its null, default to true return true; } + @Override + public String getName() { + return title; + } + + @Override + public void setName(String name) { + this.title = name; + } + + @Override + public List getChildGroups() { + return childGroups; + } + + @Override + public List getChildEntries() { + return childEntries; + } + + @Override + public void setGroups(List groups) { + childGroups = groups; + } + + @Override + public void setEntries(List entries) { + childEntries = entries; + } + + @Override + public void addChildGroup(PwGroupInterface group) { + this.childGroups.add(group); + } + + @Override + public void addChildEntry(PwEntryInterface entry) { + this.childEntries.add(entry); + } + + @Override + public PwGroupInterface getChildGroupAt(int number) { + return this.childGroups.get(number); + } + + @Override + public PwEntryInterface getChildEntryAt(int number) { + return this.childEntries.get(number); + } + + @Override + public void removeChildGroup(PwGroupInterface group) { + this.childGroups.remove(group); + } + + @Override + public void removeChildEntry(PwEntryInterface entry) { + this.childEntries.remove(entry); + } + + @Override + public int numbersOfChildGroups() { + return childGroups.size(); + } + + @Override + public int numbersOfChildEntries() { + return childEntries.size(); + } + + @Override + public List getDirectChildren() { + List children = new ArrayList<>(childGroups); + for(PwEntryInterface child : childEntries) { + if (!child.isMetaStream()) + children.add(child); + } + return children; + } + + public boolean preOrderTraverseTree(GroupHandler groupHandler, + EntryHandler entryHandler) { + if (entryHandler != null) { + for (PwEntryInterface entry : childEntries) { + if (!entryHandler.operate(entry)) return false; + } + } + for (PwGroupInterface group : childGroups) { + if ((groupHandler != null) && !groupHandler.operate(group)) return false; + group.preOrderTraverseTree(groupHandler, entryHandler); + } + return true; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index 44450b584..d13d5b060 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -28,25 +28,38 @@ import com.kunzisoft.keepass.database.ISmallTimeLogger; import org.joda.time.LocalDate; +import java.util.UUID; + /** * Abstract class who manage Groups and Entries */ -public abstract class PwNode implements ISmallTimeLogger, Parcelable, Cloneable { +public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parcelable, Cloneable { - protected Parent parent = null; + protected PwNodeId uuid; + protected PwGroupInterface parent = null; protected PwIconStandard icon = new PwIconStandard(); protected PwDate creation = new PwDate(); protected PwDate lastMod = new PwDate(); protected PwDate lastAccess = new PwDate(); protected PwDate expireDate = PwDate.PW_NEVER_EXPIRE; - protected PwNode() {} + abstract PwNodeId initNodeId(); + + protected PwNode() { + this.uuid = initNodeId(); + } + + protected PwNode(PwGroupInterface parent) { + this(); + this.parent = parent; + } protected PwNode(Parcel in) { + uuid = in.readParcelable(PwNodeId.class.getClassLoader()); // TODO better technique ? try { - PwGroupId pwGroupId = in.readParcelable(PwGroupId.class.getClassLoader()); - parent = (Parent) App.getDB().getPwDatabase().getGroupByGroupId(pwGroupId); + PwNodeId pwGroupId = in.readParcelable(PwNodeId.class.getClassLoader()); + parent = App.getDB().getPwDatabase().getGroupByGroupId(pwGroupId); } catch (Exception e) { e.printStackTrace(); } @@ -60,7 +73,9 @@ public abstract class PwNode implements ISmallTimeLogger @Override public void writeToParcel(Parcel dest, int flags) { - PwGroupId parentId = null; + dest.writeParcelable(uuid, flags); + + PwNodeId parentId = null; if (parent != null) parentId = parent.getId(); dest.writeParcelable(parentId, flags); @@ -77,11 +92,8 @@ public abstract class PwNode implements ISmallTimeLogger return 0; } - protected void construct(Parent parent) { - this.parent = parent; - } - - protected void assign(PwNode source) { + protected void assign(PwNode source) { + this.uuid = source.uuid; this.parent = source.parent; this.icon = source.icon; this.creation = source.creation; @@ -95,6 +107,7 @@ public abstract class PwNode implements ISmallTimeLogger PwNode newNode; try { newNode = (PwNode) super.clone(); + newNode.uuid = uuid.clone(); // newNode.parent stay the same in copy newNode.icon = new PwIconStandard(this.icon); newNode.creation = creation.clone(); @@ -107,27 +120,19 @@ public abstract class PwNode implements ISmallTimeLogger return newNode; } - /** - * Type of available Nodes - */ - public enum Type { - GROUP, ENTRY - } + boolean isSameType(PwNode pwNode) { + return getType().equals(pwNode.getType()); + } - /** - * @return Type of Node - */ - public abstract Type getType(); + @Override + public UUID getUUID() { + return uuid; + } - /** - * @return Title - */ - public abstract String getTitle(); - - /** - * @return Title to display, typically return alternative title if {@link #getTitle()} is empty - */ - protected abstract String getVisualTitle(); + @Override + public void setUUID(UUID uuid) { + this.uuid = uuid; + } /** * @return Visual icon @@ -148,14 +153,14 @@ public abstract class PwNode implements ISmallTimeLogger * Retrieve the parent node * @return PwGroup parent as group */ - public Parent getParent() { + public PwGroupInterface getParent() { return parent; } /** * Assign a parent to this node */ - public void setParent(Parent prt) { + public void setParent(PwGroupInterface prt) { parent = prt; } @@ -209,28 +214,9 @@ public abstract class PwNode implements ISmallTimeLogger return expireDate.getDate().before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()); } - /** - * If the content (type, title, icon) is visually the same - * @param o Node to compare - * @return True if visually as o - */ - public boolean isContentVisuallyTheSame(PwNode o) { - return getType().equals(o.getType()) - && getVisualTitle().equals(o.getVisualTitle()) - && getIcon().equals(o.getIcon()); - } - - /** - * Define if it's the same type of another node - * @param otherNode The other node to test - * @return true if both have the same type - */ - boolean isSameType(PwNode otherNode) { - return getType() != null ? getType().equals(otherNode.getType()) : otherNode.getType() == null; - } - - public boolean isContainedIn(PwGroup container) { - PwGroup cur = this.getParent(); + @Override + public boolean isContainedIn(PwGroupInterface container) { + PwGroupInterface cur = this.getParent(); while (cur != null) { if (cur.equals(container)) { return true; @@ -240,6 +226,7 @@ public abstract class PwNode implements ISmallTimeLogger return false; } + @Override public void touch(boolean modified, boolean touchParents) { PwDate now = new PwDate(); setLastAccessTime(now); @@ -248,9 +235,24 @@ public abstract class PwNode implements ISmallTimeLogger setLastModificationTime(now); } - Parent parent = getParent(); + PwGroupInterface parent = getParent(); if (touchParents && parent != null) { parent.touch(modified, true); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PwNode pwNode = (PwNode) o; + return isSameType(pwNode) + && (getUUID() != null ? getUUID().equals(pwNode.getUUID()) : pwNode.getUUID() == null); + } + + @Override + public int hashCode() { + return getUUID() != null ? getUUID().hashCode() : 0; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupId.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java similarity index 82% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupId.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java index 31c7949e6..04c3db0de 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupId.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java @@ -22,11 +22,11 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; -public abstract class PwGroupId implements Parcelable { +public abstract class PwNodeId implements Cloneable, Parcelable { - public PwGroupId() {} + public PwNodeId() {} - public PwGroupId(Parcel in) {} + public PwNodeId(Parcel in) {} @Override public void writeToParcel(Parcel dest, int flags) {} @@ -35,4 +35,7 @@ public abstract class PwGroupId implements Parcelable { public int describeContents() { return 0; } + + @Override + public abstract PwNodeId clone() throws CloneNotSupportedException; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java similarity index 69% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV3.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java index a07e645ac..474f5a9ba 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java @@ -21,16 +21,20 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -public class PwGroupIdV3 extends PwGroupId { +public class PwNodeIdInt extends PwNodeId { private int id; - - public PwGroupIdV3(int groupId) { + + public PwNodeIdInt() { + this.id = -1; + } + + public PwNodeIdInt(int groupId) { super(); this.id = groupId; } - public PwGroupIdV3(Parcel in) { + public PwNodeIdInt(Parcel in) { super(in); id = in.readInt(); } @@ -41,24 +45,29 @@ public class PwGroupIdV3 extends PwGroupId { dest.writeInt(id); } - public static final Creator CREATOR = new Creator() { + public static final Creator CREATOR = new Creator() { @Override - public PwGroupIdV3 createFromParcel(Parcel in) { - return new PwGroupIdV3(in); + public PwNodeIdInt createFromParcel(Parcel in) { + return new PwNodeIdInt(in); } @Override - public PwGroupIdV3[] newArray(int size) { - return new PwGroupIdV3[size]; + public PwNodeIdInt[] newArray(int size) { + return new PwNodeIdInt[size]; } }; - + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + @Override public boolean equals(Object compare) { - if ( ! (compare instanceof PwGroupIdV3) ) { + if ( ! (compare instanceof PwNodeIdInt) ) { return false; } - PwGroupIdV3 cmp = (PwGroupIdV3) compare; + PwNodeIdInt cmp = (PwNodeIdInt) compare; return id == cmp.id; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java similarity index 68% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java index bdf136b76..25ce848db 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupIdV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java @@ -23,16 +23,20 @@ import android.os.Parcel; import java.util.UUID; -public class PwGroupIdV4 extends PwGroupId { +public class PwNodeIdUUID extends PwNodeId { private UUID uuid; + + public PwNodeIdUUID() { + this.uuid = UUID.randomUUID(); + } - public PwGroupIdV4(UUID uuid) { + public PwNodeIdUUID(UUID uuid) { super(); this.uuid = uuid; } - public PwGroupIdV4(Parcel in) { + public PwNodeIdUUID(Parcel in) { super(in); uuid = (UUID) in.readSerializable(); } @@ -43,24 +47,29 @@ public class PwGroupIdV4 extends PwGroupId { dest.writeSerializable(uuid); } - public static final Creator CREATOR = new Creator() { + public static final Creator CREATOR = new Creator() { @Override - public PwGroupIdV4 createFromParcel(Parcel in) { - return new PwGroupIdV4(in); + public PwNodeIdUUID createFromParcel(Parcel in) { + return new PwNodeIdUUID(in); } @Override - public PwGroupIdV4[] newArray(int size) { - return new PwGroupIdV4[size]; + public PwNodeIdUUID[] newArray(int size) { + return new PwNodeIdUUID[size]; } }; + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + @Override public boolean equals(Object id) { - if ( ! (id instanceof PwGroupIdV4) ) { + if ( ! (id instanceof PwNodeIdUUID) ) { return false; } - PwGroupIdV4 v4 = (PwGroupIdV4) id; + PwNodeIdUUID v4 = (PwNodeIdUUID) id; return uuid.equals(v4.uuid); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java new file mode 100644 index 000000000..ab010f163 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java @@ -0,0 +1,48 @@ +package com.kunzisoft.keepass.database.element; + +import java.util.UUID; + +public interface PwNodeInterface { + + UUID getUUID(); + + void setUUID(UUID uuid); + + String getName(); + + void setName(String title); + + /** + * @return Visual icon + */ + PwIcon getIcon(); + + /** + * @return Type of Node + */ + PwNodeInterface.Type getType(); + + /** + * Retrieve the parent node + * @return PwGroup parent as group + */ + PwGroupInterface getParent(); + + /** + * Assign a parent to this node + */ + void setParent(PwGroupInterface prt); + + void touch(boolean modified, boolean touchParents); + + boolean isContainedIn(PwGroupInterface container); + + boolean isSearchingEnabled(); + + /** + * Type of available Nodes + */ + enum Type { + GROUP, ENTRY + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java index 34b827c4a..4911b9f7a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.iterator; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.search.SearchParameters; @@ -29,7 +29,7 @@ import java.util.Iterator; public abstract class EntrySearchStringIterator implements Iterator { - public static EntrySearchStringIterator getInstance(PwEntry e) { + public static EntrySearchStringIterator getInstance(PwEntryInterface e) { if (e instanceof PwEntryV3) { return new EntrySearchStringIteratorV3((PwEntryV3) e); } else if (e instanceof PwEntryV4) { @@ -39,7 +39,7 @@ public abstract class EntrySearchStringIterator implements Iterator { } } - public static EntrySearchStringIterator getInstance(PwEntry e, SearchParameters sp) { + public static EntrySearchStringIterator getInstance(PwEntryInterface e, SearchParameters sp) { if (e instanceof PwEntryV3) { return new EntrySearchStringIteratorV3((PwEntryV3) e, sp); } else if (e instanceof PwEntryV4) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java index 2c3465ce7..3c9eb55df 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java @@ -101,7 +101,7 @@ public class EntrySearchStringIteratorV3 extends EntrySearchStringIterator { private String getCurrentString() { switch (current) { case title: - return entry.getTitle(); + return entry.getName(); case url: return entry.getUrl(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index b4ed6802d..439dbe41b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -356,7 +356,7 @@ public class ImporterV3 extends Importer { ent.setIconStandard(db.getIconFactory().getIcon(iconId)); break; case 0x0004 : - ent.setTitle(Types.readCString(buf, offset)); + ent.setName(Types.readCString(buf, offset)); break; case 0x0005 : ent.setUrl(Types.readCString(buf, offset)); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java index 22ab3b80b..ec8b74d67 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java @@ -89,7 +89,7 @@ public class PwEntryOutputV3 { // Title //byte[] title = mPE.title.getBytes("UTF-8"); mOS.write(TITLE_FIELD_TYPE); - int titleLen = Types.writeCString(mPE.getTitle(), mOS); + int titleLen = Types.writeCString(mPE.getName(), mOS); outputBytes += titleLen; // URL diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java index b96201c55..b0d37a13d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java @@ -20,13 +20,13 @@ package com.kunzisoft.keepass.database.search; import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import java.util.Date; import java.util.List; -public abstract class EntrySearchHandler extends EntryHandler { +public abstract class EntrySearchHandler extends EntryHandler { protected List listStorage; protected SearchParameters sp; @@ -38,11 +38,11 @@ public abstract class EntrySearchHandler extends EntryHandler this.now = new Date(); } - protected boolean searchID(PwEntry entry) { + protected boolean searchID(PwEntryInterface entry) { return false; } - protected boolean searchStrings(PwEntry entry, String term) { + protected boolean searchStrings(PwEntryInterface entry, String term) { EntrySearchStringIterator iter = EntrySearchStringIterator.getInstance(entry, sp); while (iter.hasNext()) { String str = iter.next(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java index 6513683d3..1ace6be6e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java @@ -20,12 +20,12 @@ package com.kunzisoft.keepass.database.search; import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import java.util.Date; import java.util.List; -public class EntrySearchHandlerAll extends EntryHandler { +public class EntrySearchHandlerAll extends EntryHandler { private List listStorage; private SearchParameters sp; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index d49fe1ce8..2b02487f2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -19,9 +19,9 @@ */ package com.kunzisoft.keepass.database.search; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.utils.StrUtil; import com.kunzisoft.keepass.utils.UuidUtil; @@ -58,7 +58,7 @@ public class EntrySearchHandlerV4 extends EntrySearchHandler { } if (sp.searchInGroupNames) { - PwGroup parent = entry.getParent(); + PwGroupInterface parent = entry.getParent(); if (parent != null) { String groupName = parent.getName(); if (groupName != null) { @@ -83,7 +83,7 @@ public class EntrySearchHandlerV4 extends EntrySearchHandler { } @Override - protected boolean searchID(PwEntry e) { + protected boolean searchID(PwEntryInterface e) { PwEntryV4 entry = (PwEntryV4) e; if (sp.searchInUUIDs) { String hex = UuidUtil.toHexString(entry.getUUID()); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index 8ca8bd7bc..8cfe2060a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -27,12 +27,9 @@ import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDatabaseV3; import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwEntry; -import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroup; -import com.kunzisoft.keepass.database.element.PwGroupV3; -import com.kunzisoft.keepass.database.element.PwGroupV4; +import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import java.util.ArrayList; import java.util.Iterator; @@ -41,9 +38,7 @@ import java.util.List; import java.util.Locale; import java.util.Queue; -public class SearchDbHelper, - PwGroupSearch extends PwGroup, - PwEntrySearch extends PwEntry> { +public class SearchDbHelper { private final Context mCtx; @@ -56,9 +51,9 @@ public class SearchDbHelper()); @@ -68,22 +63,22 @@ public class SearchDbHelper worklist = new LinkedList<>(); + Queue worklist = new LinkedList<>(); if (pm.getRootGroup() != null) { worklist.add(pm.getRootGroup()); } while (worklist.size() != 0) { - PwGroupSearch top = worklist.remove(); + PwGroupInterface top = worklist.remove(); if (pm.isGroupSearchable(top, isOmitBackup)) { - for (PwEntrySearch entry : top.getChildEntries()) { + for (PwEntryInterface entry : top.getChildEntries()) { processEntries(entry, group.getChildEntries(), qStr, loc); if (group.numbersOfChildEntries() >= max) return group; } - for (PwGroupSearch childGroup : top.getChildGroups()) { + for (PwGroupInterface childGroup : top.getChildGroups()) { if (childGroup != null) { worklist.add(childGroup); } @@ -94,9 +89,9 @@ public class SearchDbHelper results, String qStr, Locale loc) { + private void processEntries(PwEntryInterface entry, List results, String qStr, Locale loc) { // Search all strings in the entry - Iterator iter = entry.stringIterator(); + Iterator iter = EntrySearchStringIterator.getInstance(entry); while (iter.hasNext()) { String str = iter.next(); if (str != null && str.length() != 0) { @@ -109,14 +104,14 @@ public class SearchDbHelper{ + public static class SearchDbHelperV3 extends SearchDbHelper{ public SearchDbHelperV3(Context ctx) { super(ctx); } } - public static class SearchDbHelperV4 extends SearchDbHelper{ + public static class SearchDbHelperV4 extends SearchDbHelper{ public SearchDbHelperV4(Context ctx) { super(ctx); diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index c8f6ace74..aca3e8176 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -36,7 +36,7 @@ import android.widget.Toast; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwGroup; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwIcon; import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION; @@ -79,7 +79,7 @@ public class GroupEditDialogFragment extends DialogFragment return fragment; } - public static GroupEditDialogFragment build(PwGroup group) { + public static GroupEditDialogFragment build(PwGroupInterface group) { Bundle bundle = new Bundle(); bundle.putString(KEY_NAME, group.getName()); bundle.putParcelable(KEY_ICON, group.getIcon()); diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index b0a667322..b5c06fd38 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.utils; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwEntry; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.search.EntrySearchV4; import com.kunzisoft.keepass.database.search.SearchParametersV4; @@ -46,7 +46,7 @@ public class SprEngineV4 { } } - public String compile(String text, PwEntry entry, PwDatabase database) { + public String compile(String text, PwEntryInterface entry, PwDatabase database) { SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, (PwEntryV4)entry); return compileInternal(text, ctx, 0); @@ -84,7 +84,7 @@ public class SprEngineV4 { String data; switch (wanted) { case 'T': - data = found.getTitle(); + data = found.getName(); break; case 'U': data = found.getUsername(); From ce3e1c86bccf0c7576b474121e4dc611aa28c36d Mon Sep 17 00:00:00 2001 From: Rui Mendes Date: Wed, 20 Mar 2019 13:00:47 +0000 Subject: [PATCH 085/289] Translated using Weblate (Portuguese (Portugal)) Currently translated at 45.5% (157 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pt_PT/ --- app/src/main/res/values-pt-rPT/strings.xml | 89 +++++++++++----------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 396a291c9..aa396852e 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -1,4 +1,4 @@ - + - - Comentários: - Página Web: +--> + Comentários + Página inicial Uma implementação do gestor de palavras-chave KeePass para Android Aceitar Adicionar entrada Adicionar grupo Adicionar linha - Algoritmo - Tempo de espera da aplicação - Tempo antes do bloqueio da base de dados quando a aplicação está inativa. - Aplicação + Algoritmo de encriptação + Tempo de espera da app + Inatividade antes da app ser bloqueada + App Definições da aplicação Não mostrar novamente Parênteses - A exploração de ficheiros requer o gestor de ficheiros Open Itents, clique em baixo para instalar. Devido a algumas particularidades no gestor de ficheiros, a exploração pode não funcionar corretamente na primeira vez. + Explore ficheiros instalando o gestor de ficheiros OpenIntents Cancelar Área de transferência limpa. Erro na área de transferência - Alguns dispositivos Samsung Android têm um erro na implementação da área de transferência que causa falhas na cópia a partir de aplicações. Para mais de detalhes vá a: - A limpeza da área de transferência falhou + Alguns dispositivos Samsung Android não deixam as apps usarem a área de transferência. + Não foi possível limpar a área de transferência Tempo de espera da área de transferência - Tempo antes da limpeza da área de transferência depois de copiado o nome de utilizador ou a palavra-passe. + Duração do armazenamento na área de transferência Selecione para copiar %1$s para a área de transferência A criar a chave da base de dados… Base de dados - A desencriptar conteúdo da base de dados… - Utilizar esta base de dados como predefinida + A desencriptar o conteúdo da base de dados… + Utilizar como base de dados predefinida Dígitos - KeePass DX \u00A9 %1$d Kunzisoft vem com ABSOLUTAMENTE NENHUMA GARANTIA; Este software é livre, e pode redistribui-lo conforme as condições da licença GPL versão 3 ou superior. + KeePass DX © %1$d Kunzisoft é fornecido ABSOLUTAMENTE SEM NENHUMA GARANTIA; Este programa é livre e pode redistribui-lo conforme as condições da licença GPL versão 3 ou superior. Introduza o nome do ficheiro da base de dados Acedido Cancelar - Comentários + Notas Confirmar palavra-chave Criado Expira Ficheiro chave Modificado - Dados da entrada não encontrados. + Não foi possível encontrar os dados da entrada. Palavra-chave Guardar Nome URL Nome de utilizador - A cifra de fluxo ArcFour não é suportada. + A cifra de fluxo ARCFOUR não é suportada. O KeePass DX não suporta esta URI. Não foi possível criar diretório pai. Este ficheiro já existe. - Falha ao abrir hiperligação. - É obrigatório introduzir um nome para o ficheiro. + Não foi possível abrir a hiperligação. + Introduza um nome para o ficheiro. Não foi possível criar o ficheiro: - Base de dados inválida. - Caminho inválido. - É obrigatório introduzir um nome. - É obrigatório introduzir uma palavra-passe ou um ficheiro chave. - O dispositivo ficou sem memória ao analisar a base de dados. Poderá ser muito grande para o seu dispositivo. + Não foi possível ler a base de dados. + Certifique-se que o caminho é válido. + Introduza um nome. + Selecione um ficheiro chave. + Sem memória para carregar toda a bases de dados. Pelo menos um tipo de geração de palavra-chave deve ser selecionado. - As palavra-passe não coincidem. + As palavras-passe não coincidem. As rondas devem ser um número. Rondas muito grandes. A definir para 2147483648. É obrigatório introduzir um nome para o campo de cada linha. - É obrigatório introduzir um título. + Adicione um título. Introduza um número inteiro positivo no campo do comprimento Nome do campo Valor do campo @@ -161,26 +160,24 @@ O formato .kdb apenas suporta o conjunto de caracteres Latin1. A sua palavra-chave pode conter caracteres fora deste conjunto. Todos os caracteres não Latin1 são convertidos para o mesmo carácter, o que reduz a segurança da sua palavra-chave. É recomendável alterar a sua palavra-chave. O cartão SD não se encontra montado no seu dispositivo. Não será possível abrir ou criar a sua base de dados. Versão %1$s - Introduza a palavra-passe e/ou o ficheiro chave para desbloquear a base de dados. - - 5 segundos - 10 segundos - 20 segundos - 30 segundos - 1 minuto - 5 minutos - 15 minutos - 30 minutos - Nunca + 5 segundos + 10 segundos + 20 segundos + 30 segundos + 1 minuto + 5 minutos + 15 minutos + 30 minutos + Nunca - Pequena - Média - Grande + Pequena + Média + Grande -ASCII Estendido + ASCII Estendido Permitir Não foi possível abrir a sua base de dados. Não foi possível carregar a chave. Tente baixar \"Uso de Memória\" do KDF. @@ -228,4 +225,8 @@ Definir como serviço de preenchimento automático padrão Ativar o serviço para preencher formulários em outras aplicações Tamanho da palavra-chave gerada - + Editar entrada + Encriptação + Função de derivação de chave + Deslize para o lado para limpar agora a área de transferência + \ No newline at end of file From a376d2f28604bc1808ca4c33ca08e04e05079210 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 21 Mar 2019 17:58:08 +0100 Subject: [PATCH 086/289] Refactor Entry and Group --- .../kunzisoft/keepass/tests/PwDateTest.java | 2 +- .../keepass/tests/PwEntryTestV3.java | 2 +- .../keepass/tests/PwEntryTestV4.java | 4 +- .../kunzisoft/keepass/tests/PwGroupTest.java | 2 +- .../keepass/tests/database/DeleteEntry.java | 8 +- .../keepass/tests/database/EntryV4.java | 8 +- .../keepass/tests/database/SprEngineTest.java | 5 +- .../keepass/activities/EntryActivity.java | 29 +++-- .../keepass/activities/EntryEditActivity.java | 29 ++--- .../keepass/activities/GroupActivity.java | 45 ++++--- .../keepass/adapters/NodeAdapter.java | 4 +- .../adapters/SearchEntryCursorAdapter.java | 2 +- .../keepass/autofill/AutofillHelper.kt | 10 +- .../keepass/database/BinaryPool.java | 29 +++-- .../keepass/database/GroupHandler.java | 2 +- .../keepass/database/ITimeLogger.java | 2 +- ...imeLogger.java => SmallTimeInterface.java} | 2 +- .../keepass/database/SortNodeEnum.java | 8 +- .../keepass/database/cursor/EntryCursor.java | 25 ++-- .../keepass/database/element/Database.java | 18 ++- .../keepass/database/element/PwDatabase.java | 18 +-- .../database/element/PwDatabaseV3.java | 21 ++-- .../database/element/PwDatabaseV4.java | 10 +- .../database/element/PwDbHeaderV4.java | 10 +- .../database/element/PwDeletedObject.java | 11 +- .../database/element/PwEntryInterface.java | 47 +++---- .../keepass/database/element/PwEntryV3.java | 115 ++++++------------ .../keepass/database/element/PwEntryV4.java | 30 ++++- .../database/element/PwGroupInterface.java | 29 +++-- .../keepass/database/element/PwGroupV3.java | 59 +++------ .../keepass/database/element/PwGroupV4.java | 50 +++----- .../keepass/database/element/PwIcon.java | 13 +- .../database/element/PwIconCustom.java | 12 +- .../database/element/PwIconStandard.java | 8 +- .../keepass/database/element/PwNode.java | 66 +++++----- .../keepass/database/element/PwNodeId.java | 8 +- .../keepass/database/element/PwNodeIdInt.java | 25 ++-- .../database/element/PwNodeIdUUID.java | 11 +- .../database/element/PwNodeInterface.java | 18 ++- .../iterator/EntrySearchStringIteratorV3.java | 2 +- .../keepass/database/load/ImporterV3.java | 14 +-- .../keepass/database/load/ImporterV4.java | 24 ++-- .../keepass/database/save/PwDbV3Output.java | 11 +- .../keepass/database/save/PwDbV4Output.java | 101 +++++++-------- .../database/save/PwEntryOutputV3.java | 9 +- .../database/save/PwGroupOutputV3.java | 6 +- .../database/search/EntrySearchHandler.java | 62 ---------- .../search/EntrySearchHandlerAll.java | 54 -------- .../database/search/EntrySearchHandlerV4.java | 43 +++++-- .../database/search/EntrySearchV4.java | 104 ---------------- .../database/search/SearchDbHelper.java | 2 +- .../dialogs/GroupEditDialogFragment.java | 2 +- .../kunzisoft/keepass/utils/SprContextV4.java | 3 +- .../kunzisoft/keepass/utils/SprEngineV4.java | 74 +++++++++-- 54 files changed, 568 insertions(+), 740 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/database/{ISmallTimeLogger.java => SmallTimeInterface.java} (96%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java index 069e8af7e..16363e0f4 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java @@ -27,7 +27,7 @@ public class PwDateTest extends TestCase { public void testDate() { PwDate jDate = new PwDate(System.currentTimeMillis()); - PwDate intermediate = (PwDate) jDate.clone(); + PwDate intermediate = jDate.clone(); PwDate cDate = new PwDate(intermediate.getCDate(), 0); diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java index 66c647a68..7cea7bc36 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java @@ -42,7 +42,7 @@ public class PwEntryTestV3 extends AndroidTestCase { } public void testName() { - assertTrue("Name was " + mPE.getName(), mPE.getName().equals("Amazon")); + assertTrue("Name was " + mPE.getTitle(), mPE.getTitle().equals("Amazon")); } public void testPassword() throws UnsupportedEncodingException { diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java index a6237abf6..f8b0fba06 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java @@ -33,6 +33,8 @@ import java.util.UUID; public class PwEntryTestV4 extends TestCase { public void testAssign() { + /* + TODO Test PwEntryV4 entry = new PwEntryV4(); entry.setAdditional("test223"); @@ -53,7 +55,7 @@ public class PwEntryTestV4 extends TestCase { entry.setParent(new PwGroupV4()); entry.addExtraField("key2", new ProtectedString(false, "value2")); entry.setUrl("http://localhost"); - entry.setUUID(UUID.randomUUID()); + entry.setNodeId(UUID.randomUUID()); PwEntryV4 target = new PwEntryV4(); target.updateWith(entry); diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java index db98a6371..fe0b5d4bc 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java @@ -38,7 +38,7 @@ public class PwGroupTest extends AndroidTestCase { } public void testGroupName() { - assertTrue("Name was " + mPG.getName(), mPG.getName().equals("Internet")); + assertTrue("Name was " + mPG.getTitle(), mPG.getTitle().equals("Internet")); } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index 3c5fd82bc..b97a4c2ab 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -85,14 +85,16 @@ public class DeleteEntry extends AndroidTestCase { } private PwEntryV3 getEntry(PwDatabaseV3 pm, String name) { + /* + TODO test List entries = pm.getEntries(); for ( int i = 0; i < entries.size(); i++ ) { PwEntryV3 entry = entries.get(i); - if ( entry.getName().equals(name) ) { + if ( entry.getTitle().equals(name) ) { return entry; } } - + */ return null; } @@ -101,7 +103,7 @@ public class DeleteEntry extends AndroidTestCase { List groups = pm.getGroups(); for ( int i = 0; i < groups.size(); i++ ) { PwGroupInterface group = groups.get(i); - if ( group.getName().equals(name) ) { + if ( group.getTitle().equals(name) ) { return group; } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java index b44e08d61..d15671cf7 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java @@ -33,21 +33,21 @@ public class EntryV4 extends TestCase { PwEntryV4 entry = new PwEntryV4(); entry.startToManageFieldReferences(db); - entry.setName("Title1"); + entry.setTitle("Title1"); entry.setUsername("User1"); entry.createBackup(db); - entry.setName("Title2"); + entry.setTitle("Title2"); entry.setUsername("User2"); entry.createBackup(db); - entry.setName("Title3"); + entry.setTitle("Title3"); entry.setUsername("User3"); entry.createBackup(db); PwEntryV4 backup = entry.getHistory().get(0); entry.stopToManageFieldReferences(); - assertEquals("Title2", backup.getName()); + assertEquals("Title2", backup.getTitle()); assertEquals("User2", backup.getUsername()); } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java index d666c6895..738319a0a 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java @@ -62,12 +62,15 @@ public class SprEngineTest extends AndroidTestCase { private final String ENCODE_UUID = "IN7RkON49Ui1UZ2ddqmLcw=="; private final String RESULT = "Password"; public void testRefReplace() { + /* + TODO TEST UUID entryUUID = decodeUUID(ENCODE_UUID); - PwEntryV4 entry = (PwEntryV4) db.getEntryByUUIDId(entryUUID); + PwEntryV4 entry = (PwEntryV4) db.getEntryById(entryUUID); assertEquals(RESULT, spr.compile(REF, entry, db)); + */ } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 37c36e0f3..00371a3a6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -47,6 +47,7 @@ import com.kunzisoft.keepass.database.ExtraFields; import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.notifications.NotificationCopyingService; import com.kunzisoft.keepass.notifications.NotificationField; @@ -56,13 +57,11 @@ import com.kunzisoft.keepass.timeout.ClipboardHelper; import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.utils.Types; import com.kunzisoft.keepass.utils.Util; import com.kunzisoft.keepass.view.EntryContentsView; import java.util.ArrayList; import java.util.Date; -import java.util.UUID; import static com.kunzisoft.keepass.settings.PreferencesUtil.isClipboardNotificationsEnable; import static com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields; @@ -88,7 +87,7 @@ public class EntryActivity extends LockingHideActivity { public static void launch(Activity activity, PwEntryInterface pw, boolean readOnly) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryActivity.class); - intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pw.getUUID())); + intent.putExtra(KEY_ENTRY, pw.getNodeId()); ReadOnlyHelper.INSTANCE.putReadOnlyInIntent(intent, readOnly); activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE); } @@ -114,13 +113,19 @@ public class EntryActivity extends LockingHideActivity { // Get Entry from UUID Intent i = getIntent(); - UUID uuid = Types.bytestoUUID(i.getByteArrayExtra(KEY_ENTRY)); - mEntry = db.getPwDatabase().getEntryByUUIDId(uuid); - if (mEntry == null) { - Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show(); - finish(); - return; - } + PwNodeId keyEntry; + try { + keyEntry = i.getParcelableExtra(KEY_ENTRY); + mEntry = db.getPwDatabase().getEntryById(keyEntry); + } catch (ClassCastException e) { + Log.e(TAG, "Unable to retrieve the entry key"); + } finally { + if (mEntry == null) { + Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show(); + finish(); + return; + } + } // Retrieve the textColor to tint the icon int[] attrs = {R.attr.textColorInverse}; @@ -180,8 +185,8 @@ public class EntryActivity extends LockingHideActivity { // username already copied, waiting for user's action before copy password. Intent intent = new Intent(this, NotificationCopyingService.class); intent.setAction(NotificationCopyingService.ACTION_NEW_NOTIFICATION); - if (mEntry.getName() != null) - intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getName()); + if (mEntry.getTitle() != null) + intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getTitle()); // Construct notification fields ArrayList notificationFields = new ArrayList<>(); // Add username if exists to notifications diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 1eef01b94..3c3318719 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -50,6 +50,7 @@ import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDate; import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.PwIcon; import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwIconStandard; @@ -60,14 +61,11 @@ import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.utils.Types; import com.kunzisoft.keepass.utils.Util; import com.kunzisoft.keepass.view.EntryEditCustomField; import org.jetbrains.annotations.NotNull; -import java.util.UUID; - import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD; public class EntryEditActivity extends LockingHideActivity @@ -117,7 +115,7 @@ public class EntryEditActivity extends LockingHideActivity public static void launch(Activity activity, PwEntryInterface pwEntry) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryEditActivity.class); - intent.putExtra(KEY_ENTRY, Types.UUIDtoBytes(pwEntry.getUUID())); + intent.putExtra(KEY_ENTRY, pwEntry.getNodeId()); activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); } } @@ -131,7 +129,7 @@ public class EntryEditActivity extends LockingHideActivity public static void launch(Activity activity, PwGroupInterface pwGroup) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryEditActivity.class); - intent.putExtra(KEY_PARENT, pwGroup.getId()); + intent.putExtra(KEY_PARENT, pwGroup.getNodeId()); activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); } } @@ -174,7 +172,7 @@ public class EntryEditActivity extends LockingHideActivity database = App.getDB(); Intent intent = getIntent(); - byte[] uuidBytes = intent.getByteArrayExtra(KEY_ENTRY); + PwNodeId keyEntry = intent.getParcelableExtra(KEY_ENTRY); // Retrieve the textColor to tint the icon int[] attrs = {android.R.attr.textColorPrimary}; @@ -184,7 +182,7 @@ public class EntryEditActivity extends LockingHideActivity mSelectedIconStandard = database.getPwDatabase().getIconFactory().getUnknownIcon(); PwDatabase pm = database.getPwDatabase(); - if ( uuidBytes == null ) { + if (keyEntry == null) { PwNodeId parentId = intent.getParcelableExtra(KEY_PARENT); PwGroupInterface parent = pm.getGroupByGroupId(parentId); mEntry = database.createEntry(parent); @@ -192,8 +190,7 @@ public class EntryEditActivity extends LockingHideActivity // Add the default icon database.getDrawFactory().assignDefaultDatabaseIconTo(this, entryIconView, iconColor); } else { - UUID uuid = Types.bytestoUUID(uuidBytes); - mEntry = pm.getEntryByUUIDId(uuid); + mEntry = pm.getEntryById(keyEntry); mIsNew = false; fillData(); } @@ -416,7 +413,7 @@ public class EntryEditActivity extends LockingHideActivity protected PwEntryInterface populateNewEntry() { PwDatabase db = App.getDB().getPwDatabase(); - PwEntryInterface newEntry = mEntry.clone(); + PwEntryInterface newEntry = mEntry.duplicate(); newEntry.startToManageFieldReferences(db); @@ -425,8 +422,8 @@ public class EntryEditActivity extends LockingHideActivity newEntry.setLastAccessTime(new PwDate()); newEntry.setLastModificationTime(new PwDate()); - newEntry.setName(entryTitleView.getText().toString()); - newEntry.setIconStandard(retrieveIcon()); + newEntry.setTitle(entryTitleView.getText().toString()); + newEntry.setIcon(retrieveIcon()); newEntry.setUrl(entryUrlView.getText().toString()); newEntry.setUsername(entryUserNameView.getText().toString()); @@ -454,7 +451,7 @@ public class EntryEditActivity extends LockingHideActivity /** * Retrieve the icon by the selection, or the first icon in the list if the entry is new or the last one */ - private PwIconStandard retrieveIcon() { + private PwIcon retrieveIcon() { if (!mSelectedIconStandard.isUnknown()) return mSelectedIconStandard; @@ -464,7 +461,7 @@ public class EntryEditActivity extends LockingHideActivity } else { // Keep previous icon, if no new one was selected - return mEntry.getIconStandard(); + return mEntry.getIcon(); } } } @@ -513,7 +510,7 @@ public class EntryEditActivity extends LockingHideActivity // Don't start the field reference manager, we want to see the raw ref mEntry.stopToManageFieldReferences(); - entryTitleView.setText(mEntry.getName()); + entryTitleView.setText(mEntry.getTitle()); entryUserNameView.setText(mEntry.getUsername()); entryUrlView.setText(mEntry.getUrl()); String password = mEntry.getPassword(); @@ -543,7 +540,7 @@ public class EntryEditActivity extends LockingHideActivity @Override public void iconPicked(Bundle bundle) { mSelectedIconStandard = bundle.getParcelable(KEY_ICON_STANDARD); - mEntry.setIconStandard(mSelectedIconStandard); + mEntry.setIcon(mSelectedIconStandard); assignIconView(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 6421d9cb8..1c9336c2c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -76,9 +76,7 @@ import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwIconStandard; import com.kunzisoft.keepass.database.element.PwNodeInterface; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; @@ -89,6 +87,8 @@ import com.kunzisoft.keepass.dialogs.SortDialogFragment; import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.magikeyboard.MagikIME; +import com.kunzisoft.keepass.model.Entry; +import com.kunzisoft.keepass.model.Field; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; @@ -145,7 +145,7 @@ public class GroupActivity extends LockingActivity if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, GroupActivity.class); if (group != null) { - intent.putExtra(GROUP_ID_KEY, group.getId()); + intent.putExtra(GROUP_ID_KEY, group.getNodeId()); } ReadOnlyHelper.INSTANCE.putReadOnlyInIntent(intent, readOnly); intentBuildLauncher.launchActivity(intent); @@ -364,7 +364,7 @@ public class GroupActivity extends LockingActivity @Override protected void onSaveInstanceState(Bundle outState) { if (mCurrentGroup != null) - outState.putParcelable(GROUP_ID_KEY, mCurrentGroup.getId()); + outState.putParcelable(GROUP_ID_KEY, mCurrentGroup.getNodeId()); outState.putParcelable(OLD_GROUP_TO_UPDATE_KEY, oldGroupToUpdate); if (nodeToCopy != null) outState.putParcelable(NODE_TO_COPY_KEY, nodeToCopy); @@ -407,7 +407,7 @@ public class GroupActivity extends LockingActivity public void assignGroupViewElements() { // Assign title if (mCurrentGroup != null) { - String title = mCurrentGroup.getName(); + String title = mCurrentGroup.getTitle(); if (title != null && title.length() > 0) { if (groupNameView != null) { groupNameView.setText(title); @@ -502,7 +502,7 @@ public class GroupActivity extends LockingActivity return null; }, () -> { - MagikIME.setEntryKey(entry.getEntry()); + MagikIME.setEntryKey(getEntry(entry)); // Show the notification if allowed in Preferences if (PreferencesUtil.enableKeyboardNotificationEntry(GroupActivity.this)) { startService(new Intent( @@ -529,6 +529,21 @@ public class GroupActivity extends LockingActivity } } + private Entry getEntry(PwEntryInterface entry) { + Entry entryModel = new Entry(); + entryModel.setTitle(entry.getTitle()); + entryModel.setUsername(entry.getUsername()); + entryModel.setPassword(entry.getPassword()); + entryModel.setUrl(entry.getUrl()); + if (entry.containsCustomFields()) { + entry.getFields() + .doActionToAllCustomProtectedField( + (key, value) -> entryModel.addCustomField( + new Field(key, value.toString()))); + } + return entryModel; + } + @Override public boolean onOpenMenuClick(PwNodeInterface node) { onNodeClick(node); @@ -956,18 +971,14 @@ public class GroupActivity extends LockingActivity String name, PwIcon icon) { Database database = App.getDB(); - PwIconStandard iconStandard = database.getPwDatabase().getIconFactory().getFolderIcon(); switch (action) { case CREATION: // If group creation // Build the group PwGroupInterface newGroup = database.createGroup(mCurrentGroup); - newGroup.setName(name); - try { - iconStandard = (PwIconStandard) icon; - } catch (Exception ignored) {} // TODO custom icon - newGroup.setIconStandard(iconStandard); + newGroup.setTitle(name); + newGroup.setIcon(icon); // If group created save it in the database new Thread(new AddGroupRunnable(this, @@ -982,15 +993,9 @@ public class GroupActivity extends LockingActivity // If update add new elements if (oldGroupToUpdate != null) { PwGroupInterface updateGroup = oldGroupToUpdate.duplicate(); - try { - iconStandard = (PwIconStandard) icon; - updateGroup = ((PwGroupV4) oldGroupToUpdate).duplicate(); // TODO generalize - } catch (Exception e) { - e.printStackTrace(); - } - updateGroup.setName(name); + updateGroup.setTitle(name); // TODO custom icon - updateGroup.setIconStandard(iconStandard); + updateGroup.setIcon(icon); if (listNodesFragment != null) listNodesFragment.removeNode(oldGroupToUpdate); diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index f8652a602..5ff83b9b9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -91,7 +91,7 @@ public class NodeAdapter extends RecyclerView.Adapter { } @Override public boolean areContentsTheSame(PwNodeInterface oldItem, PwNodeInterface newItem) { - return oldItem.getName().equals(newItem.getName()) + return oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getIcon().equals(newItem.getIcon()); } @@ -235,7 +235,7 @@ public class NodeAdapter extends RecyclerView.Adapter { } database.getDrawFactory().assignDatabaseIconTo(context, holder.icon, subNode.getIcon(), iconColor); // Assign text - holder.text.setText(subNode.getName()); + holder.text.setText(subNode.getTitle()); // Assign click holder.container.setOnClickListener( new OnNodeClickListener(subNode)); diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java index bcaa6ca5d..c6f05252c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java @@ -104,7 +104,7 @@ public class SearchEntryCursorAdapter extends CursorAdapter { database.getDrawFactory().assignDatabaseIconTo(context, viewHolder.imageViewIcon, icon, iconColor); // Assign title - String showTitle = PwEntryInterface.getVisualTitle(false, title, username, url, uuid); + String showTitle = PwEntryInterface.getVisualTitle(false, title, username, url, uuid.toString()); viewHolder.textViewTitle.setText(showTitle); if (displayUsername && !username.isEmpty()) { viewHolder.textViewSubTitle.setText(String.format("(%s)", username)); diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt index c126925d4..26c610e88 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -52,10 +52,10 @@ object AutofillHelper { } private fun makeEntryTitle(entry: PwEntryInterface): String { - if (!entry.name.isEmpty() && !entry.username.isEmpty()) - return String.format("%s (%s)", entry.name, entry.username) - if (!entry.name.isEmpty()) - return entry.name + if (!entry.title.isEmpty() && !entry.username.isEmpty()) + return String.format("%s (%s)", entry.title, entry.username) + if (!entry.title.isEmpty()) + return entry.title if (!entry.username.isEmpty()) return entry.username return if (!entry.notes.isEmpty()) entry.notes.trim { it <= ' ' } else "" @@ -67,7 +67,7 @@ object AutofillHelper { val title = makeEntryTitle(entry) val views = newRemoteViews(context.packageName, title) val builder = Dataset.Builder(views) - builder.setId(entry.uuid.toString()) + builder.setId(entry.nodeId.toString()) if (entry.password != null) { val value = AutofillValue.forText(entry.password) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java index ddbe51f13..fb6f544b9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java @@ -19,7 +19,9 @@ */ package com.kunzisoft.keepass.database; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.security.ProtectedBinary; @@ -62,19 +64,6 @@ public class BinaryPool { return pool.values(); } - private class AddBinaries extends EntryHandler { - - @Override - public boolean operate(PwEntryV4 entry) { - for (PwEntryV4 histEntry : entry.getHistory()) { - add(histEntry.getBinaries()); - } - add(entry.getBinaries()); - return true; - } - - } - private void add(Map dict) { for (ProtectedBinary pb : dict.values()) { add(pb); @@ -105,7 +94,17 @@ public class BinaryPool { } private void build(PwGroupV4 rootGroup) { - EntryHandler eh = new AddBinaries(); - rootGroup.preOrderTraverseTree(null, eh); + PwGroupInterface.preOrderTraverseTree(rootGroup, null, new EntryHandler() { + @Override + public boolean operate(PwEntryInterface entryInterface) { + PwEntryV4 entry = (PwEntryV4) entryInterface; + + for (PwEntryV4 histEntry : entry.getHistory()) { + add(histEntry.getBinaries()); + } + add(entry.getBinaries()); + return true; + } + }); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/GroupHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/GroupHandler.java index 3b23e22a8..8e83b4f1c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/GroupHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/GroupHandler.java @@ -26,5 +26,5 @@ package com.kunzisoft.keepass.database; * */ public abstract class GroupHandler { - public abstract boolean operate(T entry); + public abstract boolean operate(T group); } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java b/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java index 35117cbe7..df4ce2290 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database; import com.kunzisoft.keepass.database.element.PwDate; -public interface ITimeLogger extends ISmallTimeLogger { +public interface ITimeLogger extends SmallTimeInterface { long getUsageCount(); void setUsageCount(long count); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ISmallTimeLogger.java b/app/src/main/java/com/kunzisoft/keepass/database/SmallTimeInterface.java similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/database/ISmallTimeLogger.java rename to app/src/main/java/com/kunzisoft/keepass/database/SmallTimeInterface.java index b01d3c890..fe192bbe4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ISmallTimeLogger.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/SmallTimeInterface.java @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database; import com.kunzisoft.keepass.database.element.PwDate; -public interface ISmallTimeLogger { +public interface SmallTimeInterface { PwDate getLastModificationTime(); void setLastModificationTime(PwDate date); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java index dc2f46267..b59df0f16 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java @@ -113,8 +113,8 @@ public enum SortNodeEnum { new EntryNameComparator(ascending), object1, object2, - object1.getName() - .compareToIgnoreCase(object2.getName())); + object1.getTitle() + .compareToIgnoreCase(object2.getTitle())); } } @@ -215,7 +215,7 @@ public enum SortNodeEnum { if (object1.equals(object2)) return 0; - int groupNameComp = object1.getName().compareToIgnoreCase(object2.getName()); + int groupNameComp = object1.getTitle().compareToIgnoreCase(object2.getTitle()); // If same name, can be different if (groupNameComp == 0) { return object1.hashCode() - object2.hashCode(); @@ -310,7 +310,7 @@ public enum SortNodeEnum { if (object1.equals(object2)) return 0; - int entryTitleComp = object1.getName().compareToIgnoreCase(object2.getName()); + int entryTitleComp = object1.getTitle().compareToIgnoreCase(object2.getTitle()); // If same title, can be different if (entryTitleComp == 0) { return object1.hashCode() - object2.hashCode(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java index 358b96c8f..4260d3857 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java @@ -10,6 +10,7 @@ import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.element.PwIconCustom; import com.kunzisoft.keepass.database.element.PwIconFactory; import com.kunzisoft.keepass.database.element.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwNodeIdUUID; import java.util.UUID; @@ -48,10 +49,10 @@ public class EntryCursor extends MatrixCursor { public void addEntry(PwEntryV3 entry) { addRow(new Object[] {entryId, - entry.getUUID().getMostSignificantBits(), - entry.getUUID().getLeastSignificantBits(), - entry.getName(), - entry.getIconStandard().getIconId(), + entry.getNodeId().getId().getMostSignificantBits(), + entry.getNodeId().getId().getLeastSignificantBits(), + entry.getTitle(), + entry.getIcon().getIconId(), PwDatabase.UUID_ZERO.getMostSignificantBits(), PwDatabase.UUID_ZERO.getLeastSignificantBits(), entry.getUsername(), @@ -63,10 +64,10 @@ public class EntryCursor extends MatrixCursor { public void addEntry(PwEntryV4 entry) { addRow(new Object[] {entryId, - entry.getUUID().getMostSignificantBits(), - entry.getUUID().getLeastSignificantBits(), - entry.getName(), - entry.getIconStandard().getIconId(), + entry.getNodeId().getId().getMostSignificantBits(), + entry.getNodeId().getId().getLeastSignificantBits(), + entry.getTitle(), + entry.getIcon().getIconId(), entry.getIconCustom().getUUID().getMostSignificantBits(), entry.getIconCustom().getUUID().getLeastSignificantBits(), entry.getUsername(), @@ -82,13 +83,13 @@ public class EntryCursor extends MatrixCursor { } private void populateEntryBaseVersion(PwEntryInterface pwEntry, PwIconFactory iconFactory) { - pwEntry.setUUID( + pwEntry.setNodeId(new PwNodeIdUUID( new UUID(getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), - getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS)))); - pwEntry.setName(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE))); + getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))))); + pwEntry.setTitle(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE))); PwIconStandard iconStandard = iconFactory.getIcon(getInt(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_STANDARD))); - pwEntry.setIconStandard(iconStandard); + pwEntry.setIcon(iconStandard); pwEntry.setUsername(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_USERNAME))); pwEntry.setPassword(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_PASSWORD))); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 570558ec0..97e94d158 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -153,17 +153,13 @@ public class Database { pwDatabase = databaseImporter.openDatabase(bis, password, keyFileInputStream, progressTaskUpdater); if ( pwDatabase != null ) { try { + pwDatabase.populateGlobals(pwDatabase.getRootGroup()); + passwordEncodingError = !pwDatabase.validatePasswordEncoding(password); switch (pwDatabase.getVersion()) { case V3: - PwGroupV3 rootV3 = ((PwDatabaseV3) pwDatabase).getRootGroup(); - ((PwDatabaseV3) pwDatabase).populateGlobals(rootV3); - passwordEncodingError = !pwDatabase.validatePasswordEncoding(password); searchHelper = new SearchDbHelper.SearchDbHelperV3(ctx); break; case V4: - PwGroupV4 rootV4 = ((PwDatabaseV4) pwDatabase).getRootGroup(); - ((PwDatabaseV4) pwDatabase).populateGlobals(rootV4); - passwordEncodingError = !pwDatabase.validatePasswordEncoding(password); searchHelper = new SearchDbHelper.SearchDbHelperV4(ctx); break; } @@ -549,7 +545,7 @@ public class Database { case V4: newPwGroup = new PwGroupV4((PwGroupV4) parent); } - newPwGroup.setId(pwDatabase.newGroupId()); + newPwGroup.setNodeId(pwDatabase.newGroupId()); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be created", e); } @@ -708,14 +704,14 @@ public class Database { switch (getPwDatabase().getVersion()) { case V3: PwEntryV3 entryV3Copied = ((PwEntryV3) entryToCopy).clone(); - entryV3Copied.setUUID(UUID.randomUUID()); - entryV3Copied.setParent((PwGroupV3) newParent); + entryV3Copied.setNodeId(new PwNodeIdUUID()); + entryV3Copied.setParent(newParent); addEntryTo(entryV3Copied, newParent); return entryV3Copied; case V4: PwEntryV4 entryV4Copied = ((PwEntryV4) entryToCopy).clone(); - entryV4Copied.setUUID(UUID.randomUUID()); - entryV4Copied.setParent((PwGroupV4) newParent); + entryV4Copied.setNodeId(new PwNodeIdUUID()); + entryV4Copied.setParent(newParent); addEntryTo(entryV4Copied, newParent); return entryV4Copied; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index 2ab7127e0..b1ad708ae 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -49,7 +49,7 @@ public abstract class PwDatabase { protected PwIconFactory iconFactory = new PwIconFactory(); protected Map groups = new HashMap<>(); - protected Map entries = new HashMap<>(); + protected Map entries = new HashMap<>(); private static boolean isKDBExtension(String filename) { if (filename == null) { return false; } @@ -259,7 +259,7 @@ public abstract class PwDatabase { parent.addChildGroup(newGroup); newGroup.setParent(parent); - groups.put(newGroup.getId(), newGroup); + groups.put(newGroup.getNodeId(), newGroup); parent.touch(true, true); } @@ -269,7 +269,7 @@ public abstract class PwDatabase { if (parent != null) { parent.removeChildGroup(remove); } - groups.remove(remove.getId()); + groups.remove(remove.getNodeId()); } public abstract PwNodeId newGroupId(); @@ -290,7 +290,7 @@ public abstract class PwDatabase { for (int i = 0; i < groups.size(); i++) { PwGroupInterface group =groups.get(i); - if (group.getId().equals(id)) { + if (group.getNodeId().equals(id)) { return true; } } @@ -302,7 +302,7 @@ public abstract class PwDatabase { public abstract List getEntries(); - public PwEntryInterface getEntryByUUIDId(UUID id) { + public PwEntryInterface getEntryById(PwNodeId id) { return this.entries.get(id); } @@ -313,7 +313,7 @@ public abstract class PwDatabase { } newEntry.setParent(parent); - entries.put(newEntry.getUUID(), newEntry); + entries.put(newEntry.getNodeId(), newEntry); } protected void removeEntryFrom(PwEntryInterface remove, PwGroupInterface parent) { @@ -321,7 +321,7 @@ public abstract class PwDatabase { if (parent != null) { parent.removeChildEntry(remove); } - entries.remove(remove.getUUID()); + entries.remove(remove.getNodeId()); } public abstract boolean isBackup(PwGroupInterface group); @@ -333,12 +333,12 @@ public abstract class PwDatabase { for (int i = 0; i < childEntries.size(); i++ ) { PwEntryInterface cur = childEntries.get(i); - entries.put(cur.getUUID(), cur); + entries.put(cur.getNodeId(), cur); } for (int i = 0; i < childGroups.size(); i++ ) { PwGroupInterface cur = childGroups.get(i); - groups.put(cur.getId(), cur); + groups.put(cur.getNodeId(), cur); populateGlobals(cur); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index f7c9f0ae4..dbf97467a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -89,9 +89,9 @@ public class PwDatabaseV3 extends PwDatabase { private void initAndAddGroup(String title, int iconId, PwGroupInterface parent) { PwGroupV3 group = createGroup(); - group.setId(newGroupId()); - group.setName(title); - group.setIconStandard(iconFactory.getIcon(iconId)); + group.setNodeId(newGroupId()); + group.setTitle(title); + group.setIcon(iconFactory.getIcon(iconId)); addGroupTo(group, parent); } @@ -153,7 +153,7 @@ public class PwDatabaseV3 extends PwDatabase { return kids; } - public List getGrpChildren(PwGroupInterface parent) { + private List getGrpChildren(PwGroupInterface parent) { int idx = groups.indexOf(parent); int target = parent.getLevel() + 1; List kids = new ArrayList<>(); @@ -167,7 +167,7 @@ public class PwDatabaseV3 extends PwDatabase { return kids; } - public List getEntries(PwGroupInterface parent) { + private List getEntries(PwGroupInterface parent) { List kids = new ArrayList<>(); /* * for( Iterator i = entries.iterator(); i.hasNext(); ) { PwEntryV3 ent @@ -176,7 +176,7 @@ public class PwDatabaseV3 extends PwDatabase { */ for (int i = 0; i < entries.size(); i++) { PwEntryInterface ent = entries.get(i); - if (ent.getParent().getGroupId() == parent.getGroupId()) + if (ent.getParent().getNodeId().equals(parent.getNodeId())) kids.add(ent); } return kids; @@ -234,10 +234,9 @@ public class PwDatabaseV3 extends PwDatabase { public PwNodeIdInt newGroupId() { PwNodeIdInt newId; Random random = new Random(); - while (true) { + do { newId = new PwNodeIdInt(random.nextInt()); - if (!isGroupIdUsed(newId)) break; - } + } while (isGroupIdUsed(newId)); return newId; } @@ -359,7 +358,7 @@ public class PwDatabaseV3 extends PwDatabase { @Override public boolean isBackup(PwGroupInterface group) { while (group != null) { - if (group.getLevel() == 0 && group.getName().equalsIgnoreCase("Backup")) { + if (group.getLevel() == 0 && group.getTitle().equalsIgnoreCase("Backup")) { return true; } group = group.getParent(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 822489f94..397db3cf5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -553,7 +553,7 @@ public class PwDatabaseV4 extends PwDatabase { @Override public void populateGlobals(PwGroupInterface currentGroup) { - groups.put(rootGroup.getId(), rootGroup); + groups.put(rootGroup.getNodeId(), rootGroup); super.populateGlobals(currentGroup); } @@ -572,7 +572,7 @@ public class PwDatabaseV4 extends PwDatabase { recycleBin.setExpanded(false); addGroupTo(recycleBin, rootGroup); - recycleBinUUID = recycleBin.getUUID(); + recycleBinUUID = recycleBin.getNodeId().getId(); } } @@ -684,13 +684,13 @@ public class PwDatabaseV4 extends PwDatabase { @Override public void deleteEntry(PwEntryInterface entry) { super.deleteEntry(entry); - deletedObjects.add(new PwDeletedObject(entry.getUUID())); + deletedObjects.add(new PwDeletedObject((UUID) entry.getNodeId().getId())); } @Override public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface origParent) { super.undoDeleteEntry(entry, origParent); - deletedObjects.remove(new PwDeletedObject(entry.getUUID())); + deletedObjects.remove(new PwDeletedObject((UUID) entry.getNodeId().getId())); } @Override @@ -740,7 +740,7 @@ public class PwDatabaseV4 extends PwDatabase { @Override public void initNew(String dbPath) { rootGroup = new PwGroupV4(dbNameFromPath(dbPath), iconFactory.getFolderIcon()); - groups.put(rootGroup.getId(), rootGroup); + groups.put(rootGroup.getNodeId(), rootGroup); } private String dbNameFromPath(String dbPath) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java index 54096f664..8a73ff91e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java @@ -109,12 +109,12 @@ public class PwDbHeaderV4 extends PwDbHeader { this.version = version; } - private class GroupHasCustomData extends GroupHandler { + private class GroupHasCustomData extends GroupHandler { boolean hasCustomData = false; @Override - public boolean operate(PwGroupV4 group) { + public boolean operate(PwGroupInterface group) { if (group == null) { return true; } @@ -127,12 +127,12 @@ public class PwDbHeaderV4 extends PwDbHeader { } } - private class EntryHasCustomData extends EntryHandler { + private class EntryHasCustomData extends EntryHandler { boolean hasCustomData = false; @Override - public boolean operate(PwEntryV4 entry) { + public boolean operate(PwEntryInterface entry) { if (entry == null) { return true; } @@ -164,7 +164,7 @@ public class PwDbHeaderV4 extends PwDbHeader { if (databaseV4.getRootGroup() == null ) { return PwDbHeaderV4.FILE_VERSION_32_3; } - databaseV4.getRootGroup().preOrderTraverseTree(groupHandler, entryHandler); + PwGroupInterface.preOrderTraverseTree(databaseV4.getRootGroup(), groupHandler, entryHandler); if (groupHandler.hasCustomData || entryHandler.hasCustomData) { return PwDbHeaderV4.FILE_VERSION_32_4; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java index 8a85d3c5f..e842838e5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java @@ -23,11 +23,12 @@ import java.util.Date; import java.util.UUID; public class PwDeletedObject { + public UUID uuid; private Date deletionTime; public PwDeletedObject() { - + } public PwDeletedObject(UUID u) { @@ -38,6 +39,14 @@ public class PwDeletedObject { uuid = u; deletionTime = d; } + + public UUID getUuid() { + return uuid; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } public Date getDeletionTime() { if ( deletionTime == null ) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java index 49f74156e..3857ce6c0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java @@ -1,20 +1,13 @@ package com.kunzisoft.keepass.database.element; import com.kunzisoft.keepass.database.ExtraFields; +import com.kunzisoft.keepass.database.SmallTimeInterface; import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.model.Entry; -import com.kunzisoft.keepass.model.Field; -import java.util.UUID; +public interface PwEntryInterface extends PwNodeInterface, SmallTimeInterface { -public interface PwEntryInterface extends PwNodeInterface { - - UUID getUUID(); - - void setUUID(UUID uuid); - - String getName(); - void setName(String title); + String getTitle(); + void setTitle(String title); String getUsername(); void setUsername(String user); @@ -91,7 +84,7 @@ public interface PwEntryInterface extends PwNodeInterface { String PMS_TAN_ENTRY = ""; static boolean isTan(PwEntryInterface entry) { - return entry.getName().equals(PMS_TAN_ENTRY) + return entry.getTitle().equals(PMS_TAN_ENTRY) && (entry.getUsername().length() > 0); } @@ -101,15 +94,18 @@ public interface PwEntryInterface extends PwNodeInterface { * {@link #startToManageFieldReferences(PwDatabase)} and {@link #stopToManageFieldReferences()} must be called * before and after {@link #getVisualTitle(PwEntryInterface entry)} */ - static String getVisualTitle(boolean isTan, String userName, String title, String url, UUID uuid) { + static String getVisualTitle(boolean isTan, String title, String userName, String url, String id) { if ( isTan ) { return PMS_TAN_ENTRY + " " + userName; } else { if (title.isEmpty()) - if (url.isEmpty()) - return uuid.toString(); + if (userName.isEmpty()) + if (url.isEmpty()) + return id; + else + return url; else - return url; + return userName; else return title; } @@ -117,24 +113,9 @@ public interface PwEntryInterface extends PwNodeInterface { static String getVisualTitle(PwEntryInterface entry) { return getVisualTitle(PwEntryInterface.isTan(entry), + entry.getTitle(), entry.getUsername(), - entry.getName(), entry.getUrl(), - entry.getUUID()); - } - - static Entry getEntry(PwEntryInterface entry) { - Entry entryModel = new Entry(); - entryModel.setTitle(entry.getName()); - entryModel.setUsername(entry.getUsername()); - entryModel.setPassword(entry.getPassword()); - entryModel.setUrl(entry.getUrl()); - if (entry.containsCustomFields()) { - entry.getFields() - .doActionToAllCustomProtectedField( - (key, value) -> entryModel.addCustomField( - new Field(key, value.toString()))); - } - return entryModel; + entry.getNodeId().toString()); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index da5b84117..ba18e1d77 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -72,7 +72,7 @@ import java.util.UUID; * @author Dominik Reichl * @author Jeremy Jamet */ -public class PwEntryV3 extends PwNode implements PwEntryInterface { +public class PwEntryV3 extends PwNode implements PwEntryInterface { /** Size of byte buffer needed to hold this struct. */ private static final String PMS_ID_BINDESC = "bin-stream"; @@ -80,14 +80,19 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { private static final String PMS_ID_USER = "SYSTEM"; private static final String PMS_ID_URL = "$"; - private String title; - private String username; - private byte[] password; - private String url; - private String additional; + private String title = ""; + private String username = ""; + private byte[] password = new byte[0]; + private String url = ""; + private String additional = ""; /** A string describing what is in pBinaryData */ - private String binaryDesc; - private byte[] binaryData; + private String binaryDesc = ""; + private byte[] binaryData = new byte[0]; + + @Override + PwNodeId initNodeId() { + return new PwNodeIdUUID(); + } public PwEntryV3() { super(); @@ -188,17 +193,14 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { return Type.ENTRY; } - public void setGroupId(int groupId) { - PwGroupV3 parentGroup = new PwGroupV3(); - parentGroup.setGroupId(groupId); - this.parent = parentGroup; + public void setNewParent(int groupId) { + PwGroupV3 pwGroupV3 = new PwGroupV3(); + pwGroupV3.setNodeId(new PwNodeIdInt(groupId)); + this.parent = pwGroupV3; } @Override public String getUsername() { - if (username == null) { - return ""; - } return username; } @@ -208,12 +210,12 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { } @Override - public String getName() { + public String getTitle() { return title; } @Override - public void setName(String title) { + public void setTitle(String title) { this.title = title; } @@ -237,53 +239,11 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { this.url = url; } - public void populateBlankFields(PwDatabaseV3 db) { - // TODO verify and remove - if (icon == null) { - icon = db.getIconFactory().getKeyIcon(); - } - - if (username == null) { - username = ""; - } - - if (password == null) { - password = new byte[0]; - } - - if (uuid == null) { - uuid = UUID.randomUUID(); - } - - if (title == null) { - title = ""; - } - - if (url == null) { - url = ""; - } - - if (additional == null) { - additional = ""; - } - - if (binaryDesc == null) { - binaryDesc = ""; - } - - if (binaryData == null) { - binaryData = new byte[0]; - } - } - /** * @return the actual password byte array. */ @Override public String getPassword() { - if (password == null) { - return ""; - } return new String(password); } @@ -301,10 +261,7 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { /** Securely erase old password before copying new. */ public void setPassword( byte[] buf, int offset, int len ) { - if( password != null ) { - fill( password, (byte)0 ); - password = null; - } + fill(password, (byte)0); password = new byte[len]; System.arraycopy( buf, offset, password, 0, len ); } @@ -316,7 +273,6 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { password = pass.getBytes("UTF-8"); setPassword(password, 0, password.length); } catch (UnsupportedEncodingException e) { - assert false; password = pass.getBytes(); setPassword(password, 0, password.length); } @@ -350,19 +306,17 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { // Determine if this is a MetaStream entry @Override public boolean isMetaStream() { - if ( binaryData == null ) return false; - if ( additional == null || additional.length() == 0 ) return false; - if ( ! binaryDesc.equals(PMS_ID_BINDESC) ) return false; - if ( title == null ) return false; - if ( ! title.equals(PMS_ID_TITLE) ) return false; - if ( username == null ) return false; - if ( ! username.equals(PMS_ID_USER) ) return false; - if ( url == null ) return false; - if ( ! url.equals(PMS_ID_URL)) return false; - if ( !icon.isMetaStreamIcon() ) return false; - - return true; - } + if (binaryData == new byte[0]) return false; + if (additional.isEmpty()) return false; + if (!binaryDesc.equals(PMS_ID_BINDESC)) return false; + if (title.isEmpty()) return false; + if (!title.equals(PMS_ID_TITLE)) return false; + if (username.isEmpty()) return false; + if (!username.equals(PMS_ID_USER)) return false; + if (url.isEmpty()) return false; + if (!url.equals(PMS_ID_URL)) return false; + return icon.isMetaStreamIcon(); + } @Override public void createBackup(PwDatabase db) {} @@ -372,7 +326,12 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { return false; } - @Override + @Override + public boolean containsCustomData() { + return false; + } + + @Override public void startToManageFieldReferences(PwDatabase db) {} @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 964407cd8..3f2c14bb8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -34,8 +34,9 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.UUID; -public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { +public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { public static final String STR_TITLE = "Title"; public static final String STR_USERNAME = "UserName"; @@ -62,6 +63,11 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { private String additional = ""; private String tags = ""; + @Override + PwNodeId initNodeId() { + return new PwNodeIdUUID(); + } + public PwEntryV4() { super(); } @@ -207,7 +213,7 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { } @Override - public String getName() { + public String getTitle() { return decodeRefKey(mDecodeRef, STR_TITLE); } @@ -217,7 +223,7 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { } @Override - public void setName(String title) { + public void setTitle(String title) { boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectTitle; setProtectedString(STR_TITLE, title, protect); } @@ -289,14 +295,26 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { } } - public void setIconCustom(PwIconCustom icon) { - this.customIcon = icon; - } + @Override + public void setIcon(PwIcon icon) { + if (icon instanceof PwIconStandard) + setIconStandard((PwIconStandard) icon); + if (icon instanceof PwIconCustom) + setIconCustom((PwIconCustom) icon); + } public PwIconCustom getIconCustom() { return customIcon; } + public void setIconCustom(PwIconCustom icon) { + this.customIcon = icon; + } + + public PwIcon getIconStandard() { + return icon; + } + public void setIconStandard(PwIconStandard icon) { this.icon = icon; this.customIcon = PwIconCustom.ZERO; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java index 97427b427..a9df8516b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java @@ -1,5 +1,7 @@ package com.kunzisoft.keepass.database.element; +import android.support.annotation.NonNull; + import com.kunzisoft.keepass.database.EntryHandler; import com.kunzisoft.keepass.database.GroupHandler; @@ -7,9 +9,6 @@ import java.util.List; public interface PwGroupInterface extends PwNodeInterface { - PwNodeId getId(); - void setId(PwNodeId id); - List getChildGroups(); List getChildEntries(); @@ -18,6 +17,8 @@ public interface PwGroupInterface extends PwNodeInterface { void setEntries(List entries); + int getLevel(); + void addChildGroup(PwGroupInterface group); void addChildEntry(PwEntryInterface entry); @@ -34,16 +35,30 @@ public interface PwGroupInterface extends PwNodeInterface { int numbersOfChildEntries(); + boolean containsParent(); + /** * Filter MetaStream entries and return children * @return List of direct children (one level below) as PwNode */ - public List getDirectChildren(); + List getDirectChildren(); PwGroupInterface duplicate(); - public boolean preOrderTraverseTree(GroupHandler groupHandler, - EntryHandler entryHandler); + static boolean preOrderTraverseTree(@NonNull PwGroupInterface root, + GroupHandler groupHandler, + EntryHandler entryHandler) { + if (entryHandler != null) { + for (PwEntryInterface entry : root.getChildEntries()) { + if (!entryHandler.operate(entry)) return false; + } + } + for (PwGroupInterface group : root.getChildGroups()) { + if ((groupHandler != null) && !groupHandler.operate(group)) return false; + preOrderTraverseTree(group, groupHandler, entryHandler); + } + return true; + } - public boolean allowAddEntryIfIsRoot(); + boolean allowAddEntryIfIsRoot(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 8aa6db0a5..1ad12ee43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -22,25 +22,26 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; - import java.util.ArrayList; import java.util.List; -public class PwGroupV3 extends PwNode implements PwGroupInterface { +public class PwGroupV3 extends PwNode implements PwGroupInterface { // TODO verify children not needed transient private List childGroups = new ArrayList<>(); transient private List childEntries = new ArrayList<>(); // for tree traversing - // TODO private int groupId; private String title = ""; private int level = 0; // short /** Used by KeePass internally, don't use */ private int flags; + @Override + PwNodeId initNodeId() { + return new PwNodeIdInt(); + } + public PwGroupV3() { super(); } @@ -52,7 +53,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { public PwGroupV3(Parcel in) { super(in); title = in.readString(); - groupId = in.readInt(); level = in.readInt(); flags = in.readInt(); } @@ -61,7 +61,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(title); - dest.writeInt(groupId); dest.writeInt(level); dest.writeInt(flags); } @@ -81,7 +80,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { protected void updateWith(PwGroupV3 source) { super.assign(source); title = source.title; - groupId = source.groupId; level = source.level; flags = source.flags; } @@ -101,15 +99,14 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { return clone(); } - @Override + @Override public Type getType() { return Type.GROUP; } public void setParent(PwGroupInterface parent) { super.setParent(parent); - if (parent instanceof PwGroupV3) // TODO Change - level = ((PwGroupV3) parent).getLevel() + 1; + level = parent.getLevel() + 1; } @Override @@ -117,12 +114,13 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { return false; } - public int getGroupId() { - return groupId; + @Override + public boolean containsCustomData() { + return false; } public void setGroupId(int groupId) { - this.groupId = groupId; + this.setNodeId(new PwNodeIdInt(groupId)); } public int getLevel() { @@ -133,17 +131,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { this.level = level; } - @Override - public PwNodeId getId() { - return new PwNodeIdInt(groupId); - } - - @Override - public void setId(PwNodeId id) { - PwNodeIdInt id3 = (PwNodeIdInt) id; - groupId = id3.getId(); - } - public int getFlags() { return flags; } @@ -154,7 +141,7 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { @Override public String toString() { - return getName(); + return getTitle(); } public void populateBlankFields(PwDatabaseV3 db) { @@ -169,12 +156,12 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { } @Override - public String getName() { + public String getTitle() { return title; } @Override - public void setName(String title) { + public void setTitle(String title) { this.title = title; } @@ -248,20 +235,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { return children; } - public boolean preOrderTraverseTree(GroupHandler groupHandler, - EntryHandler entryHandler) { - if (entryHandler != null) { - for (PwEntryInterface entry : childEntries) { - if (!entryHandler.operate(entry)) return false; - } - } - for (PwGroupInterface group : childGroups) { - if ((groupHandler != null) && !groupHandler.operate(group)) return false; - group.preOrderTraverseTree(groupHandler, entryHandler); - } - return true; - } - @Override public boolean allowAddEntryIfIsRoot() { return false; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index e1724ff1c..76771dad9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -21,8 +21,6 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; import com.kunzisoft.keepass.database.ITimeLogger; import java.util.ArrayList; @@ -31,7 +29,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; -public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { +public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { public static final boolean DEFAULT_SEARCHING_ENABLED = true; @@ -52,6 +50,11 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { private Boolean enableSearching = null; private UUID lastTopVisibleEntry = PwDatabase.UUID_ZERO; + @Override + PwNodeId initNodeId() { + return new PwNodeIdUUID(); + } + public PwGroupV4() { super(); } @@ -174,22 +177,10 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { } public void addEntry(PwEntryV4 pe) { - assert(pe != null); addChildEntry(pe); pe.setParent(this); } - @Override - public PwNodeId getId() { - return new PwNodeIdUUID(uuid); - } - - @Override - public void setId(PwNodeId id) { - PwNodeIdUUID id4 = (PwNodeIdUUID) id; - uuid = id4.getId(); - } - @Override public PwDate getLocationChanged() { return parentGroupLastMod; @@ -242,6 +233,10 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { this.customIcon = icon; } + public PwIcon getIconStandard() { + return icon; + } + public void setIconStandard(PwIconStandard icon) { // TODO Encapsulate with PwEntryV4 this.icon = icon; this.customIcon = PwIconCustom.ZERO; @@ -318,12 +313,12 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { } @Override - public String getName() { + public String getTitle() { return title; } @Override - public void setName(String name) { + public void setTitle(String name) { this.title = name; } @@ -347,6 +342,11 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { childEntries = entries; } + @Override + public int getLevel() { + return -1; // TODO Level + } + @Override public void addChildGroup(PwGroupInterface group) { this.childGroups.add(group); @@ -396,18 +396,4 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { } return children; } - - public boolean preOrderTraverseTree(GroupHandler groupHandler, - EntryHandler entryHandler) { - if (entryHandler != null) { - for (PwEntryInterface entry : childEntries) { - if (!entryHandler.operate(entry)) return false; - } - } - for (PwGroupInterface group : childGroups) { - if ((groupHandler != null) && !groupHandler.operate(group)) return false; - group.preOrderTraverseTree(groupHandler, entryHandler); - } - return true; - } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java index d317f4504..1430a713a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java @@ -22,7 +22,9 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; -public abstract class PwIcon implements Parcelable { +public abstract class PwIcon implements Parcelable, Cloneable { + + public static final int UNKNOWN = -1; public boolean isMetaStreamIcon() { return false; @@ -30,11 +32,16 @@ public abstract class PwIcon implements Parcelable { protected PwIcon() {} - protected PwIcon(Parcel in) {} + protected PwIcon(Parcel src) {} public abstract boolean isUnknown(); - @Override + public abstract int getIconId(); + + @Override + protected abstract PwIcon clone() throws CloneNotSupportedException; + + @Override public int describeContents() { return 0; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java index 850c75435..409e61bed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java @@ -53,6 +53,11 @@ public class PwIconCustom extends PwIcon { return uuid == null || this.equals(ZERO); } + @Override + public int getIconId() { + return UNKNOWN; + } + public UUID getUUID() { return uuid; } @@ -83,7 +88,12 @@ public class PwIconCustom extends PwIcon { } }; - @Override + @Override + protected PwIconCustom clone() throws CloneNotSupportedException { + return new PwIconCustom(this); + } + + @Override public int hashCode() { final int prime = 31; int result = 1; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java index f5f4b015d..ae4363210 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java @@ -24,7 +24,6 @@ import android.os.Parcel; public class PwIconStandard extends PwIcon { private final int iconId; - public static final int UNKNOWN = -1; public static final int KEY = 0; public static final int TRASH = 43; public static final int FOLDER = 48; @@ -77,7 +76,12 @@ public class PwIconStandard extends PwIcon { return iconId == 0; } - @Override + @Override + protected PwIconStandard clone() throws CloneNotSupportedException { + return new PwIconStandard(this); + } + + @Override public int hashCode() { final int prime = 31; int result = 1; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index d13d5b060..6f83f869d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -24,30 +24,25 @@ import android.os.Parcel; import android.os.Parcelable; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.ISmallTimeLogger; import org.joda.time.LocalDate; -import java.util.UUID; - /** * Abstract class who manage Groups and Entries */ -public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parcelable, Cloneable { +public abstract class PwNode implements PwNodeInterface, Parcelable, Cloneable { - protected PwNodeId uuid; + protected PwNodeId nodeId = initNodeId(); protected PwGroupInterface parent = null; - protected PwIconStandard icon = new PwIconStandard(); + protected PwIcon icon = new PwIconStandard(); protected PwDate creation = new PwDate(); protected PwDate lastMod = new PwDate(); protected PwDate lastAccess = new PwDate(); protected PwDate expireDate = PwDate.PW_NEVER_EXPIRE; - abstract PwNodeId initNodeId(); + abstract PwNodeId initNodeId(); - protected PwNode() { - this.uuid = initNodeId(); - } + protected PwNode() {} protected PwNode(PwGroupInterface parent) { this(); @@ -55,7 +50,7 @@ public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parce } protected PwNode(Parcel in) { - uuid = in.readParcelable(PwNodeId.class.getClassLoader()); + nodeId = in.readParcelable(PwNodeId.class.getClassLoader()); // TODO better technique ? try { PwNodeId pwGroupId = in.readParcelable(PwNodeId.class.getClassLoader()); @@ -73,11 +68,11 @@ public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parce @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(uuid, flags); + dest.writeParcelable(nodeId, flags); PwNodeId parentId = null; if (parent != null) - parentId = parent.getId(); + parentId = parent.getNodeId(); dest.writeParcelable(parentId, flags); dest.writeParcelable(icon, flags); @@ -93,7 +88,7 @@ public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parce } protected void assign(PwNode source) { - this.uuid = source.uuid; + this.nodeId = source.nodeId; this.parent = source.parent; this.icon = source.icon; this.creation = source.creation; @@ -107,9 +102,9 @@ public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parce PwNode newNode; try { newNode = (PwNode) super.clone(); - newNode.uuid = uuid.clone(); + newNode.nodeId = nodeId.clone(); // newNode.parent stay the same in copy - newNode.icon = new PwIconStandard(this.icon); + newNode.icon = icon.clone(); newNode.creation = creation.clone(); newNode.lastMod = lastMod.clone(); newNode.lastAccess = lastAccess.clone(); @@ -120,32 +115,29 @@ public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parce return newNode; } - boolean isSameType(PwNode pwNode) { - return getType().equals(pwNode.getType()); + public IdType getId() { + return getNodeId().getId(); + } + + @Override + public PwNodeId getNodeId() { + return nodeId; } @Override - public UUID getUUID() { - return uuid; - } - - @Override - public void setUUID(UUID uuid) { - this.uuid = uuid; + public void setNodeId(PwNodeId id) { + this.nodeId = id; } /** * @return Visual icon */ public PwIcon getIcon() { - return getIconStandard(); - } - - public PwIconStandard getIconStandard() { return icon; } - public void setIconStandard(PwIconStandard icon) { + @Override + public void setIcon(PwIcon icon) { this.icon = icon; } @@ -160,12 +152,12 @@ public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parce /** * Assign a parent to this node */ - public void setParent(PwGroupInterface prt) { - parent = prt; + public void setParent(PwGroupInterface parent) { + this.parent = parent; } /** - * @return true if parent is present (can be a root or a detach element) + * @return true if parent is present (false if not present, can be a root or a detach element) */ public boolean containsParent() { return getParent() != null; @@ -241,18 +233,18 @@ public abstract class PwNode implements PwNodeInterface, ISmallTimeLogger, Parce } } - @Override + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PwNode pwNode = (PwNode) o; - return isSameType(pwNode) - && (getUUID() != null ? getUUID().equals(pwNode.getUUID()) : pwNode.getUUID() == null); + return getType().equals(pwNode.getType()) + && (getNodeId() != null ? getNodeId().equals(pwNode.getNodeId()) : pwNode.getNodeId() == null); } @Override public int hashCode() { - return getUUID() != null ? getUUID().hashCode() : 0; + return getNodeId() != null ? getNodeId().hashCode() : 0; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java index 04c3db0de..46f74819d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; -public abstract class PwNodeId implements Cloneable, Parcelable { +public abstract class PwNodeId implements Cloneable, Parcelable { public PwNodeId() {} @@ -36,6 +36,10 @@ public abstract class PwNodeId implements Cloneable, Parcelable { return 0; } + public abstract Id getId(); + @Override - public abstract PwNodeId clone() throws CloneNotSupportedException; + public PwNodeId clone() throws CloneNotSupportedException { + return (PwNodeId) super.clone(); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java index 474f5a9ba..c4782a394 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -21,9 +21,9 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -public class PwNodeIdInt extends PwNodeId { +public class PwNodeIdInt extends PwNodeId { - private int id; + private Integer id; public PwNodeIdInt() { this.id = -1; @@ -35,7 +35,6 @@ public class PwNodeIdInt extends PwNodeId { } public PwNodeIdInt(Parcel in) { - super(in); id = in.readInt(); } @@ -58,8 +57,8 @@ public class PwNodeIdInt extends PwNodeId { }; @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); + public PwNodeIdInt clone() throws CloneNotSupportedException { + return (PwNodeIdInt) super.clone(); } @Override @@ -68,16 +67,20 @@ public class PwNodeIdInt extends PwNodeId { return false; } PwNodeIdInt cmp = (PwNodeIdInt) compare; - return id == cmp.id; + return id.equals(cmp.id); } @Override public int hashCode() { - Integer i = id; - return i.hashCode(); + return id.hashCode(); } - - public int getId() { + + @Override + public String toString() { + return id.toString(); + } + + public Integer getId() { return id; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java index 25ce848db..6a2edb6d6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java @@ -23,7 +23,7 @@ import android.os.Parcel; import java.util.UUID; -public class PwNodeIdUUID extends PwNodeId { +public class PwNodeIdUUID extends PwNodeId { private UUID uuid; @@ -60,8 +60,8 @@ public class PwNodeIdUUID extends PwNodeId { }; @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); + public PwNodeIdUUID clone() throws CloneNotSupportedException { + return (PwNodeIdUUID) super.clone(); } @Override @@ -78,6 +78,11 @@ public class PwNodeIdUUID extends PwNodeId { return uuid.hashCode(); } + @Override + public String toString() { + return uuid.toString(); + } + public UUID getId() { return uuid; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java index ab010f163..73e96f3c4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java @@ -1,22 +1,26 @@ package com.kunzisoft.keepass.database.element; -import java.util.UUID; +import android.os.Parcelable; -public interface PwNodeInterface { +import com.kunzisoft.keepass.database.SmallTimeInterface; - UUID getUUID(); +public interface PwNodeInterface extends SmallTimeInterface, Parcelable { - void setUUID(UUID uuid); + PwNodeId getNodeId(); - String getName(); + void setNodeId(PwNodeId id); - void setName(String title); + String getTitle(); + + void setTitle(String title); /** * @return Visual icon */ PwIcon getIcon(); + void setIcon(PwIcon icon); + /** * @return Type of Node */ @@ -39,6 +43,8 @@ public interface PwNodeInterface { boolean isSearchingEnabled(); + boolean containsCustomData(); + /** * Type of available Nodes */ diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java index 3c9eb55df..2c3465ce7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java @@ -101,7 +101,7 @@ public class EntrySearchStringIteratorV3 extends EntrySearchStringIterator { private String getCurrentString() { switch (current) { case title: - return entry.getName(); + return entry.getTitle(); case url: return entry.getUrl(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index 439dbe41b..0f3055104 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -56,6 +56,7 @@ import com.kunzisoft.keepass.database.element.PwDbHeaderV3; import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.database.element.PwGroupV3; +import com.kunzisoft.keepass.database.element.PwNodeIdUUID; import com.kunzisoft.keepass.database.exception.InvalidAlgorithmException; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.InvalidDBSignatureException; @@ -269,7 +270,6 @@ public class ImporterV3 extends Importer { if( fieldType == 0xFFFF ) { // End-Group record. Save group and count it. - newEnt.populateBlankFields(databaseToOpen); databaseToOpen.addEntry(newEnt); newEnt = new PwEntryV3(); i++; @@ -301,7 +301,7 @@ public class ImporterV3 extends Importer { grp.setGroupId(LEDataInputStream.readInt(buf, offset)); break; case 0x0002 : - grp.setName(Types.readCString(buf, offset)); + grp.setTitle(Types.readCString(buf, offset)); break; case 0x0003 : grp.setCreationTime(new PwDate(buf, offset)); @@ -316,7 +316,7 @@ public class ImporterV3 extends Importer { grp.setExpiryTime(new PwDate(buf, offset)); break; case 0x0007 : - grp.setIconStandard(db.getIconFactory().getIcon(LEDataInputStream.readInt(buf, offset))); + grp.setIcon(db.getIconFactory().getIcon(LEDataInputStream.readInt(buf, offset))); break; case 0x0008 : grp.setLevel(LEDataInputStream.readUShort(buf, offset)); @@ -340,10 +340,10 @@ public class ImporterV3 extends Importer { // Ignore field break; case 0x0001 : - ent.setUUID(Types.bytestoUUID(buf, offset)); + ent.setNodeId(new PwNodeIdUUID(Types.bytestoUUID(buf, offset))); break; case 0x0002 : - ent.setGroupId(LEDataInputStream.readInt(buf, offset)); + ent.setNewParent(LEDataInputStream.readInt(buf, offset)); break; case 0x0003 : int iconId = LEDataInputStream.readInt(buf, offset); @@ -353,10 +353,10 @@ public class ImporterV3 extends Importer { iconId = 0; } - ent.setIconStandard(db.getIconFactory().getIcon(iconId)); + ent.setIcon(db.getIconFactory().getIcon(iconId)); break; case 0x0004 : - ent.setName(Types.readCString(buf, offset)); + ent.setTitle(Types.readCString(buf, offset)); break; case 0x0005 : ent.setUrl(Types.readCString(buf, offset)); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 2c0cbb36e..1c17988fe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -34,6 +34,7 @@ import com.kunzisoft.keepass.database.element.PwDeletedObject; import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.element.PwIconCustom; +import com.kunzisoft.keepass.database.element.PwNodeIdUUID; import com.kunzisoft.keepass.database.exception.ArcFourException; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.InvalidPasswordException; @@ -532,9 +533,10 @@ public class ImporterV4 extends Importer { if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { if ( ctxGroups.size() != 0 ) throw new IOException("Group list should be empty."); - - db.setRootGroup(new PwGroupV4()); - ctxGroups.push(db.getRootGroup()); + + PwGroupV4 rootGroup = new PwGroupV4(); + db.setRootGroup(rootGroup); + ctxGroups.push(rootGroup); ctxGroup = ctxGroups.peek(); return SwitchContext(ctx, KdbContext.Group, xpp); @@ -547,9 +549,9 @@ public class ImporterV4 extends Importer { case Group: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { - ctxGroup.setUUID(ReadUuid(xpp)); + ctxGroup.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemName) ) { - ctxGroup.setName(ReadString(xpp)); + ctxGroup.setTitle(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemNotes) ) { ctxGroup.setNotes(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { @@ -606,7 +608,7 @@ public class ImporterV4 extends Importer { case Entry: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { - ctxEntry.setUUID(ReadUuid(xpp)); + ctxEntry.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { ctxEntry.setIconStandard(db.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { @@ -752,7 +754,7 @@ public class ImporterV4 extends Importer { case DeletedObject: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { - ctxDeletedObject.uuid = ReadUuid(xpp); + ctxDeletedObject.setUuid(ReadUuid(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDeletionTime) ) { ctxDeletedObject.setDeletionTime(ReadTime(xpp)); } else { @@ -807,8 +809,8 @@ public class ImporterV4 extends Importer { return KdbContext.CustomData; } else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - if ( ctxGroup.getUUID() == null || ctxGroup.getUUID().equals(PwDatabase.UUID_ZERO) ) { - ctxGroup.setUUID(UUID.randomUUID()); + if ( ctxGroup.getNodeId() == null || ctxGroup.getNodeId().equals(PwDatabase.UUID_ZERO) ) { + ctxGroup.setNodeId(new PwNodeIdUUID()); } ctxGroups.pop(); @@ -835,8 +837,8 @@ public class ImporterV4 extends Importer { return KdbContext.GroupCustomData; } else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { - if ( ctxEntry.getUUID() == null || ctxEntry.getUUID().equals(PwDatabase.UUID_ZERO) ) { - ctxEntry.setUUID(UUID.randomUUID()); + if ( ctxEntry.getNodeId() == null || ctxEntry.getNodeId().equals(PwDatabase.UUID_ZERO) ) { + ctxEntry.setNodeId(new PwNodeIdUUID()); } if ( entryInHistory ) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java index 811f1b1d4..51e80288e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java @@ -25,6 +25,7 @@ import com.kunzisoft.keepass.database.element.PwDbHeader; import com.kunzisoft.keepass.database.element.PwDbHeaderV3; import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.stream.LEDataOutputStream; @@ -215,9 +216,9 @@ public class PwDbV3Output extends PwDbOutput { } // Groups - List groups = mPM.getGroups(); + List groups = mPM.getGroups(); for ( int i = 0; i < groups.size(); i++ ) { - PwGroupV3 pg = groups.get(i); + PwGroupV3 pg = (PwGroupV3) groups.get(i); PwGroupOutputV3 pgo = new PwGroupOutputV3(pg, os); try { pgo.output(); @@ -239,10 +240,10 @@ public class PwDbV3Output extends PwDbOutput { } private void sortGroupsForOutput() { - List groupList = new ArrayList<>(); + List groupList = new ArrayList<>(); // Rebuild list according to coalation sorting order removing any orphaned groups - List roots = mPM.getGrpRoots(); + List roots = mPM.getGrpRoots(); for ( int i = 0; i < roots.size(); i++ ) { sortGroup(roots.get(i), groupList); } @@ -250,7 +251,7 @@ public class PwDbV3Output extends PwDbOutput { mPM.setGroups(groupList); } - private void sortGroup(PwGroupV3 group, List groupList) { + private void sortGroup(PwGroupInterface group, List groupList) { // Add current tree groupList.add(group); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index 453486e71..25b118722 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -39,7 +39,9 @@ import com.kunzisoft.keepass.database.element.PwDatabaseV4XML; import com.kunzisoft.keepass.database.element.PwDbHeaderV4; import com.kunzisoft.keepass.database.element.PwDefsV4; import com.kunzisoft.keepass.database.element.PwDeletedObject; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.element.PwIconCustom; import com.kunzisoft.keepass.database.exception.PwDbOutputException; @@ -142,54 +144,6 @@ public class PwDbV4Output extends PwDbOutput { } } - private class GroupWriter extends GroupHandler { - private Stack groupStack; - - public GroupWriter(Stack gs) { - groupStack = gs; - } - - @Override - public boolean operate(PwGroupV4 group) { - assert(group != null); - - while(true) { - try { - if (group.getParent() == groupStack.peek()) { - groupStack.push(group); - startGroup(group); - break; - } else { - groupStack.pop(); - if (groupStack.size() <= 0) return false; - endGroup(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - return true; - } - } - - private class EntryWriter extends EntryHandler { - - @Override - public boolean operate(PwEntryV4 entry) { - assert(entry != null); - - try { - writeEntry(entry, false); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - - return true; - } - - } - private void outputDatabase(OutputStream os) throws IllegalArgumentException, IllegalStateException, IOException { xml = Xml.newSerializer(); @@ -201,14 +155,49 @@ public class PwDbV4Output extends PwDbOutput { writeMeta(); - PwGroupV4 root = mPM.getRootGroup(); + PwGroupV4 root = (PwGroupV4) mPM.getRootGroup(); xml.startTag(null, PwDatabaseV4XML.ElemRoot); startGroup(root); Stack groupStack = new Stack<>(); groupStack.push(root); - - if (!root.preOrderTraverseTree(new GroupWriter(groupStack), new EntryWriter())) - throw new RuntimeException("Writing groups failed"); + + if (!PwGroupInterface.preOrderTraverseTree(root, new GroupHandler() { + @Override + public boolean operate(PwGroupInterface groupInterface) { + PwGroupV4 group = (PwGroupV4) groupInterface; + + while (true) { + try { + if (group.getParent() == groupStack.peek()) { + groupStack.push(group); + startGroup(group); + break; + } else { + groupStack.pop(); + if (groupStack.size() <= 0) return false; + endGroup(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return true; + } + }, new EntryHandler() { + @Override + public boolean operate(PwEntryInterface entryInterface) { + PwEntryV4 entry = (PwEntryV4) entryInterface; + + try { + writeEntry(entry, false); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + return true; + } + })) throw new RuntimeException("Writing groups failed"); while (groupStack.size() > 1) { xml.endTag(null, PwDatabaseV4XML.ElemGroup); @@ -347,8 +336,8 @@ public class PwDbV4Output extends PwDbOutput { private void startGroup(PwGroupV4 group) throws IllegalArgumentException, IllegalStateException, IOException { xml.startTag(null, PwDatabaseV4XML.ElemGroup); - writeObject(PwDatabaseV4XML.ElemUuid, group.getUUID()); - writeObject(PwDatabaseV4XML.ElemName, group.getName()); + writeObject(PwDatabaseV4XML.ElemUuid, group.getNodeId().getId()); + writeObject(PwDatabaseV4XML.ElemName, group.getTitle()); writeObject(PwDatabaseV4XML.ElemNotes, group.getNotes()); writeObject(PwDatabaseV4XML.ElemIcon, group.getIconStandard().getIconId()); @@ -374,7 +363,7 @@ public class PwDbV4Output extends PwDbOutput { xml.startTag(null, PwDatabaseV4XML.ElemEntry); - writeObject(PwDatabaseV4XML.ElemUuid, entry.getUUID()); + writeObject(PwDatabaseV4XML.ElemUuid, entry.getNodeId().getId()); writeObject(PwDatabaseV4XML.ElemIcon, entry.getIconStandard().getIconId()); if (!entry.getIconCustom().equals(PwIconCustom.ZERO)) { @@ -654,7 +643,7 @@ public class PwDbV4Output extends PwDbOutput { xml.startTag(null, name); - writeObject(PwDatabaseV4XML.ElemUuid, value.uuid); + writeObject(PwDatabaseV4XML.ElemUuid, value.getUuid()); writeObject(PwDatabaseV4XML.ElemDeletionTime, value.getDeletionTime()); xml.endTag(null, name); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java index ec8b74d67..c03497066 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java @@ -20,6 +20,7 @@ package com.kunzisoft.keepass.database.save; import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.utils.Types; @@ -74,22 +75,22 @@ public class PwEntryOutputV3 { // UUID mOS.write(UUID_FIELD_TYPE); mOS.write(UUID_FIELD_SIZE); - mOS.write(Types.UUIDtoBytes(mPE.getUUID())); + mOS.write(Types.UUIDtoBytes(mPE.getNodeId().getId())); // Group ID mOS.write(GROUPID_FIELD_TYPE); mOS.write(LONG_FOUR); - mOS.write(LEDataOutputStream.writeIntBuf(mPE.getParent().getGroupId())); + mOS.write(LEDataOutputStream.writeIntBuf(((PwGroupV3) mPE.getParent()).getNodeId().getId())); // Image ID mOS.write(IMAGEID_FIELD_TYPE); mOS.write(LONG_FOUR); - mOS.write(LEDataOutputStream.writeIntBuf(mPE.getIconStandard().getIconId())); + mOS.write(LEDataOutputStream.writeIntBuf(mPE.getIcon().getIconId())); // Title //byte[] title = mPE.title.getBytes("UTF-8"); mOS.write(TITLE_FIELD_TYPE); - int titleLen = Types.writeCString(mPE.getName(), mOS); + int titleLen = Types.writeCString(mPE.getTitle(), mOS); outputBytes += titleLen; // URL diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java index ed649818c..0f3f2384e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java @@ -64,11 +64,11 @@ public class PwGroupOutputV3 { // Group ID mOS.write(GROUPID_FIELD_TYPE); mOS.write(GROUPID_FIELD_SIZE); - mOS.write(LEDataOutputStream.writeIntBuf(mPG.getGroupId())); + mOS.write(LEDataOutputStream.writeIntBuf(mPG.getId())); // Name mOS.write(NAME_FIELD_TYPE); - Types.writeCString(mPG.getName(), mOS); + Types.writeCString(mPG.getTitle(), mOS); // Create date mOS.write(CREATE_FIELD_TYPE); @@ -93,7 +93,7 @@ public class PwGroupOutputV3 { // Image ID mOS.write(IMAGEID_FIELD_TYPE); mOS.write(IMAGEID_FIELD_SIZE); - mOS.write(LEDataOutputStream.writeIntBuf(mPG.getIconStandard().getIconId())); + mOS.write(LEDataOutputStream.writeIntBuf(mPG.getIcon().getIconId())); // Level mOS.write(LEVEL_FIELD_TYPE); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java deleted file mode 100644 index b0d37a13d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.search; - -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; - -import java.util.Date; -import java.util.List; - -public abstract class EntrySearchHandler extends EntryHandler { - - protected List listStorage; - protected SearchParameters sp; - protected Date now; - - protected EntrySearchHandler(SearchParameters sp, List listStorage) { - this.listStorage = listStorage; - this.sp = sp; - this.now = new Date(); - } - - protected boolean searchID(PwEntryInterface entry) { - return false; - } - - protected boolean searchStrings(PwEntryInterface entry, String term) { - EntrySearchStringIterator iter = EntrySearchStringIterator.getInstance(entry, sp); - while (iter.hasNext()) { - String str = iter.next(); - if (str != null && str.length() > 0) { - if (sp.ignoreCase) { - str = str.toLowerCase(); - } - - if (str.contains(term)) { - return true; - } - } - } - - return false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java deleted file mode 100644 index 1ace6be6e..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerAll.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.search; - -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.element.PwEntryInterface; - -import java.util.Date; -import java.util.List; - -public class EntrySearchHandlerAll extends EntryHandler { - - private List listStorage; - private SearchParameters sp; - private Date now; - - public EntrySearchHandlerAll(SearchParameters sp, List listStorage) { - this.sp = sp; - this.listStorage = listStorage; - now = new Date(); - } - - @Override - public boolean operate(T entry) { - if (sp.respectEntrySearchingDisabled && !entry.isSearchingEnabled()) { - return true; - } - - if (sp.excludeExpired && entry.isExpires() && now.after(entry.getExpiryTime().getDate())) { - return true; - } - - listStorage.add(entry); - return true; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index 2b02487f2..9e3668d39 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -19,26 +19,32 @@ */ package com.kunzisoft.keepass.database.search; +import com.kunzisoft.keepass.database.EntryHandler; import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import com.kunzisoft.keepass.utils.StrUtil; import com.kunzisoft.keepass.utils.UuidUtil; +import java.util.Date; import java.util.List; import java.util.Locale; -public class EntrySearchHandlerV4 extends EntrySearchHandler { +public class EntrySearchHandlerV4 extends EntryHandler { - private SearchParametersV4 sp; + private List listStorage; + protected SearchParametersV4 sp; + protected Date now; - protected EntrySearchHandlerV4(SearchParameters sp, List listStorage) { - super(sp, listStorage); - this.sp = (SearchParametersV4) sp; + public EntrySearchHandlerV4(SearchParametersV4 sp, List listStorage) { + this.listStorage = listStorage; + this.sp = sp; + this.now = new Date(); } @Override - public boolean operate(PwEntryV4 entry) { + public boolean operate(PwEntryInterface entry) { if (sp.respectEntrySearchingDisabled && !entry.isSearchingEnabled()) { return true; } @@ -60,7 +66,7 @@ public class EntrySearchHandlerV4 extends EntrySearchHandler { if (sp.searchInGroupNames) { PwGroupInterface parent = entry.getParent(); if (parent != null) { - String groupName = parent.getName(); + String groupName = parent.getTitle(); if (groupName != null) { if (sp.ignoreCase) { groupName = groupName.toLowerCase(); @@ -82,16 +88,31 @@ public class EntrySearchHandlerV4 extends EntrySearchHandler { return true; } - @Override - protected boolean searchID(PwEntryInterface e) { + private boolean searchID(PwEntryInterface e) { PwEntryV4 entry = (PwEntryV4) e; if (sp.searchInUUIDs) { - String hex = UuidUtil.toHexString(entry.getUUID()); + String hex = UuidUtil.toHexString(entry.getNodeId().getId()); return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0; } return false; } - + private boolean searchStrings(PwEntryInterface entry, String term) { + EntrySearchStringIterator iter = EntrySearchStringIterator.getInstance(entry, sp); + while (iter.hasNext()) { + String str = iter.next(); + if (str != null && str.length() > 0) { + if (sp.ignoreCase) { + str = str.toLowerCase(); + } + + if (str.contains(term)) { + return true; + } + } + } + + return false; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java deleted file mode 100644 index 059760b5d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchV4.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.search; - -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupV4; -import com.kunzisoft.keepass.utils.StrUtil; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -public class EntrySearchV4 { - - private PwGroupV4 root; - - public EntrySearchV4(PwGroupV4 root) { - this.root = root; - } - - public void searchEntries(SearchParameters sp, List listStorage) { - if (sp == null) { return; } - if (listStorage == null) { return; } - - List terms = StrUtil.splitSearchTerms(sp.searchString); - if (terms.size() <= 1 || sp.regularExpression) { - searchEntriesSingle(sp, listStorage); - return; - } - - // Search longest term first - Comparator stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length(); - Collections.sort(terms, stringLengthComparator); - - String fullSearch = sp.searchString; - List pg = root.getChildEntries(); - for (int i = 0; i < terms.size(); i ++) { - List pgNew = new ArrayList<>(); - - sp.searchString = terms.get(i); - - boolean negate = false; - if (sp.searchString.startsWith("-")) { - sp.searchString = sp.searchString.substring(1); - negate = sp.searchString.length() > 0; - } - - if (!searchEntriesSingle(sp, pgNew)) { - pg = null; - break; - } - - List complement = new ArrayList<>(); - if (negate) { - for (PwEntryV4 entry: pg) { - if (!pgNew.contains(entry)) { - complement.add(entry); - } - } - pg = complement; - } - else { - pg = pgNew; - } - } - - if (pg != null) { - listStorage.addAll(pg); - } - sp.searchString = fullSearch; - - } - - private boolean searchEntriesSingle(SearchParameters spIn, List listStorage) { - SearchParameters sp = (SearchParameters) spIn.clone(); - EntryHandler eh; - if (sp.searchString.length() <= 0) { - eh = new EntrySearchHandlerAll<>(sp, listStorage); - } else { - eh = new EntrySearchHandlerV4(sp, listStorage); - } - return root.preOrderTraverseTree(null, eh); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index 8cfe2060a..a4e5d8d74 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -54,7 +54,7 @@ public class SearchDbHelper { public PwGroupInterface search(PwDatabaseVersion pm, String qStr, int max) { PwGroupInterface group = pm.createGroup(); - group.setName("\"" + qStr + "\""); + group.setTitle("\"" + qStr + "\""); group.setEntries(new ArrayList<>()); // Search all entries diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index aca3e8176..3a5353157 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -81,7 +81,7 @@ public class GroupEditDialogFragment extends DialogFragment public static GroupEditDialogFragment build(PwGroupInterface group) { Bundle bundle = new Bundle(); - bundle.putString(KEY_NAME, group.getName()); + bundle.putString(KEY_NAME, group.getTitle()); bundle.putParcelable(KEY_ICON, group.getIcon()); bundle.putInt(KEY_ACTION_ID, UPDATE.ordinal()); GroupEditDialogFragment fragment = new GroupEditDialogFragment(); diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java index b5450adca..fad1e0541 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java @@ -20,6 +20,7 @@ package com.kunzisoft.keepass.utils; import com.kunzisoft.keepass.database.element.PwDatabaseV4; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; import java.util.HashMap; @@ -28,7 +29,7 @@ import java.util.Map; public class SprContextV4 implements Cloneable { public PwDatabaseV4 db; - public PwEntryV4 entry; + public PwEntryInterface entry; public Map refsCache = new HashMap<>(); public SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) { diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index b5c06fd38..8b4653644 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -23,10 +23,13 @@ import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDatabaseV4; import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.search.EntrySearchV4; +import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.search.EntrySearchHandlerV4; import com.kunzisoft.keepass.database.search.SearchParametersV4; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Map.Entry; @@ -37,10 +40,10 @@ public class SprEngineV4 { private static final String STR_REF_END = "}"; public class TargetResult { - public PwEntryV4 entry; + public PwEntryInterface entry; public char wanted; - public TargetResult(PwEntryV4 entry, char wanted) { + public TargetResult(PwEntryInterface entry, char wanted) { this.entry = entry; this.wanted = wanted; } @@ -77,14 +80,14 @@ public class SprEngineV4 { TargetResult result = findRefTarget(fullRef, ctx); if (result != null) { - PwEntryV4 found = result.entry; + PwEntryInterface found = result.entry; char wanted = result.wanted; if (found != null) { String data; switch (wanted) { case 'T': - data = found.getName(); + data = found.getTitle(); break; case 'U': data = found.getUsername(); @@ -99,7 +102,7 @@ public class SprEngineV4 { data = found.getNotes(); break; case 'I': - data = found.getUUID().toString(); + data = found.getNodeId().toString(); break; default: offset = start + 1; @@ -154,11 +157,10 @@ public class SprEngineV4 { else if (scan == 'I') { sp.searchInUUIDs = true; } else if (scan == 'O') { sp.searchInOther = true; } else { return null; } - - List list = new ArrayList<>(); + + List list = new ArrayList<>(); // TODO type parameter - EntrySearchV4 entrySearchV4 = new EntrySearchV4(ctx.db.getRootGroup()); - entrySearchV4.searchEntries(sp, list); + searchEntries(ctx.db.getRootGroup(), sp, list); if (list.size() > 0) { return new TargetResult(list.get(0), wanted); @@ -183,7 +185,57 @@ public class SprEngineV4 { } return text; - } + private void searchEntries(PwGroupInterface root, SearchParametersV4 sp, List listStorage) { + if (sp == null) { return; } + if (listStorage == null) { return; } + + List terms = StrUtil.splitSearchTerms(sp.searchString); + if (terms.size() <= 1 || sp.regularExpression) { + PwGroupInterface.preOrderTraverseTree(root, null, new EntrySearchHandlerV4(sp, listStorage)); + return; + } + + // Search longest term first + Comparator stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length(); + Collections.sort(terms, stringLengthComparator); + + String fullSearch = sp.searchString; + List pg = root.getChildEntries(); + for (int i = 0; i < terms.size(); i ++) { + List pgNew = new ArrayList<>(); + + sp.searchString = terms.get(i); + + boolean negate = false; + if (sp.searchString.startsWith("-")) { + sp.searchString = sp.searchString.substring(1); + negate = sp.searchString.length() > 0; + } + + if (!PwGroupInterface.preOrderTraverseTree(root, null, new EntrySearchHandlerV4(sp, pgNew))) { + pg = null; + break; + } + + List complement = new ArrayList<>(); + if (negate) { + for (PwEntryInterface entry: pg) { + if (!pgNew.contains(entry)) { + complement.add(entry); + } + } + pg = complement; + } + else { + pg = pgNew; + } + } + + if (pg != null) { + listStorage.addAll(pg); + } + sp.searchString = fullSearch; + } } From 6782442d8e2fd8703c0ed90362213b0a764900c3 Mon Sep 17 00:00:00 2001 From: aasami Date: Sun, 24 Mar 2019 13:46:57 +0000 Subject: [PATCH 087/289] Translated using Weblate (Slovak) Currently translated at 25.2% (87 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sk/ --- app/src/main/res/values-sk/strings.xml | 65 +++++++++++++++----------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 80353edc4..ce3885ef7 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -16,30 +16,29 @@ You should have received a copy of the GNU General Public License along with KeePass DX. If not, see . ---> - - Spätná Väzba: - Domovská Stránka: - KeePass DX je Android verzia KeePass password manažéra. +--> + Spätná väzba + Domovská stránka + Správca hesiel KeePass pre Android Prijať Pridať Záznam Pridať Skupinu - Algoritmus - Časový limit aplikácie. + Šifrovací algoritmus + Časový limit aplikácie Čas pred uzamknutím databázy, ak je aplikácia neaktívna. - Applikácia + Aplikácia Nastavenia aplikácie Konzoly Prezeranie súborov vyžaduje otvorenie Správcu súborov, kliknite nižšie pre inštalovanie. Kôli chybám v správcovi súborov, prehľadávanie nemusí pracovať správne, ak prehľadávate prvý krát. Zrušiť Schránka vyčistená. Timeout Schránky - Čas pred vyčistením schránky po skopírovaní mena alebo hesla + Čas uchovania v schránke %1$s skopírované do schránky - Vytváram databázový kľúč… + Vytváram databázový kľúč… Databáza - Dešifrujem obsah databázy… - Použiť toto ako predvolenú databázu + Dešifrujem obsah databázy… + Použiť ako predvolenú databázu Číslice KeePass DX \u00A9 %1$d Kunzisoft použitie Absolútne bez Záruky; Toto je free software, a môžete ho používať pod GPL ver. 3 alebo vyššie. Vložte názov Databázy @@ -49,13 +48,13 @@ Potvrdiť heslo Vytvorené Expirácia - Súbor Keyfile + Súbor s kľúčom Upravené Heslo Uložiť Meno URL - Užívateľské meno + Meno používateľa ArcFour stream šifra nieje podporovaná. KeePass DX nevie použiť túto uri. Neviem vytvoriť nadradený adresár. @@ -91,7 +90,7 @@ Dĺžka Dĺžka zoznamu skupiny Veľkosť textu v zozname skupín - Načítavam Databázu… + Načítavam Databázu… Malé písmená Skryť heslo Skryť heslá štandardne @@ -115,8 +114,8 @@ Otvoriť poslednú databázu : Neprehľadávať položky Vynechať skupinu \'Backup\' a Recycle Bin z výsledkov hľadania - Vytváram novú databázu… - Pracujem… + Vytváram novú databázu… + Pracujem… Zapamätať si umiestnenie keyfile Uložiť keyfile Odstrániť @@ -125,7 +124,7 @@ Šifrovacie opakovania Vyššie opakovania šifrovania dávajú vyššiu ochranu proti útokom hrubou silou, ale môžu spomaliť načítavanie a ukladanie. opakovania - Ukladám databázu… + Ukladám databázu… Miesto Hľadať DB zoradenie poradia @@ -137,23 +136,33 @@ Veľké písmená Vaša SD karta nie je momentálne pripojená k zariadeniu. Nemôžete načítať, alebo vytvoriť databázu. Version %1$s - Vložte heslo a / alebo keyfile pre odomknutie databázy. - 5 sekúnd 10 sekúnd 20 sekúnd 30 sekúnd 1 minúta - 5 minút - 15 minút - 30 minút - Nikdy + 5 minút + 15 minút + 30 minút + Nikdy - Malé - Stredné - Veľké + Malé + Stredné + Veľké - + Upraviť záznam + Pridať reťazec + Šifrovanie + Funkcia pre tvorbu kľúča + Nezobrazovať znovu + Rozšírené ASCII + Povoliť + Chyba schránky + Niektoré Samsungy nedovolia aplikáciám používať schránku. + Nepodarilo sa vymazať schránku + Schránku vymažete potiahnutím prsta cez políčko + Nenašli sa údaje záznamu + \ No newline at end of file From 811a44574a4b6436364e6a132097ef4d10192b63 Mon Sep 17 00:00:00 2001 From: Semen Turchikhin Date: Mon, 25 Mar 2019 23:34:16 +0000 Subject: [PATCH 088/289] Translated using Weblate (Russian) Currently translated at 64.1% (221 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/ --- app/src/main/res/values-ru/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 3b46fe79b..dbcc081bb 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -68,7 +68,7 @@ Не удалось открыть ссылку. Введите имя файла. Не удалось создать файл: - Неверный формат базы или неправильный мастер-ключ. + Невозможно прочитать базу. Убедитесь что путь указан верно. Введите название. Выберите файл ключа. From 31326224acaf7ba8e908344b58baea91612221d3 Mon Sep 17 00:00:00 2001 From: Ldm Public Date: Wed, 3 Apr 2019 08:19:12 +0000 Subject: [PATCH 089/289] Translated using Weblate (French) Currently translated at 99.7% (344 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/ --- app/src/main/res/values-fr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ac9739ca0..b7829d8e3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -28,7 +28,7 @@ Algorithme de chiffrement Fonction de dérivation de clé Délai d’expiration de l’application - Durée d’inactivité avant le verrouillage de la base de données + Durée d’inactivité avant de verrouiller l\'application Application Paramètres de l’application Remplissage de formulaire From 6a5ac3729df632f96de2033e47fe1422609db149 Mon Sep 17 00:00:00 2001 From: Noel Date: Wed, 3 Apr 2019 08:07:59 +0000 Subject: [PATCH 090/289] Translated using Weblate (German) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 53066e0df..cd8019cc7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -224,7 +224,7 @@ App-Design, welches in der App genutzt wird Verschlüsselung Schlüsselableitungsfunktion - Erweiterter ASCII + Erweitertes ASCII Erlauben Wischen, um Zwischenablage jetzt zu leeren Autofill-Dienst kann nicht aktiviert werden. @@ -304,7 +304,7 @@ Eintrag bearbeiten Bearbeiten Sie Ihren Eintrag mit benutzerdefinierten Feldern. Pooldaten können zwischen verschiedenen Eingabefeldern referenziert werden. Ein starkes Passwort für ihren Eintrag erstellen. - Generieren Sie ein sicheres Passwort, um es mit Ihrem Eintrag zu verknüpfen, definieren Sie es einfach nach den Kriterien des Formulars und vergessen Sie das sichere Passwort nicht. + Generieren Sie ein sicheres Passwort, um es mit Ihrem Eintrag zu verknüpfen. Definieren Sie es einfach nach den Kriterien des Formulars und vergessen Sie das sichere Passwort nicht. Benutzerdefinierte Felder hinzufügen Registrieren Sie ein einfaches, nicht mitgeliefertes Feld, indem Sie ein neues Feld ausfüllen, das Sie auch schützen können. Ihre Datenbank entsperren From 5a30c3219e3c909048050c33099c5705c8039fb5 Mon Sep 17 00:00:00 2001 From: Noel Date: Wed, 3 Apr 2019 08:11:20 +0000 Subject: [PATCH 091/289] Translated using Weblate (Spanish) Currently translated at 48.4% (167 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/ --- app/src/main/res/values-es/strings.xml | 93 +++++++++++++------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2571ad8cd..259bd6a68 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,4 +1,4 @@ - + - - Retroalimentación: - Inicio: - KeePass DX es una implementación para Android del gestor de contraseñas KeePass. +--> + Commentario + Página de inicio + Implementación para Android del gestor de contraseñas KeePass Aceptar Añadir entrada Añadir grupo Algoritmo de cifrado Tiempo de espera de la aplicación excedido - Tiempo hasta bloquear la base de datos cuando la aplicación está inactiva. + Inactividad antes del bloqueo de aplicación Aplicación Configuración de la aplicación Paréntesis - La exploración de archivos necesita Open Intents File Manager, haz clic a continuación para instalarlo. Debido a algunas peculiaridades en el gestor de archivos, puede que la exploración no funcione correctamente la primera vez que la realices. + Explora ficheros con OpenIntents File Manager Cancelar Portapapeles limpiado Portapapeles caducado - Tiempo hasta que el portapapeles se vacía después de copiar el usuario o la contraseña + Duración de almacemiento en el portapapeles Seleccionar para copiar %1$s al portapapeles Creando clave de la base de datos… Base de datos Descifrando el contenido de la base de datos… - Utilice esto como base de datos por defecto + Utilice como base de datos por defecto Dígitos KeePass DX \u00A9 %1$d Kunzisoft NO TIENE TOTAL GARANTÍA; Este es software libre, y puedes redristribuirlo bajo las condiciones de la licencia GPL version 3 o posterior. Introduzca el nombre del archivo de base de datos @@ -58,17 +57,17 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Nombre URL Nombre de usuario - Secuencia de cifrado ArcFour no soportada. - KeePass DX no puede manejar este uri. + Secuencia de cifrado ARCFOUR no es soportada. + KeePass DX no puede manejar este URI. No pudo crearse el directorio padre. Este archivo ya existe. - Fallo al abrir el enlace. + Falló al abrir el enlace. Se necesita un nombre de archivo. No se pudo crear el archivo: - Base de datos no válida. - Ruta no válida. + Impossible de leer la base de datos. + Asegúranse que la ruta es correcta. Se necesita un nombre. - Se necesita una contraseña o un archivo de clave. + Se necesita un archivo de clave. El dispositivo se quedó sin memory mientras analizada la base de datos. Puede ser demasiado grande para este dispositivo. Debe seleccionar al menos un tipo de generación de contraseñas Las contraseñas no coinciden. @@ -115,7 +114,7 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Sin resultado de búsqueda Sin manejador para esta url. Historial de archivos recientes - Recordar nombres de archivos usados recientemente + Recordar nombres de archivos usados recientemente Abrir base de datos reciente : No buscar en las entradas de copia de seguridad o papelera de reciclaje Omitir \'Backup\' y grupo de Papelera de Reciclaje en los resultados de búsqueda @@ -141,33 +140,31 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Mayúsculas Actualmente su tarjeta SD no está montada en el dispositivo. No podrá cargar o crear su base de datos. Versión %1$s - Introduzca una contraseña y/o un archivo de clave para desbloquear su base de datos. - - 5 segundos - 10 segundos - 20 segundos - 30 segundos - 1 minuto - 5 minutos - 15 minutos - 30 minutos - Nunca + 5 segundos + 10 segundos + 20 segundos + 30 segundos + 1 minuto + 5 minutos + 15 minutos + 30 minutos + Nunca - Pequeño - Mediano - Grande + Pequeño + Mediano + Grande -Añadir cadena + Añadir cadena Cifrado Función de derivación de clave No mostrar nuevamente ASCII extendido Permitir Error del portapapeles - Algunos teléfonos Samsung con Android tienen un error en la implementación del portapapeles que provoca fallos al copiar desde las aplicaciones. Para más detalles ir a: + Algunos teléfonos Samsung con Android tienen un error en la implementación del portapapeles que provoca fallos al copiar desde las aplicaciones. Falló la limpieza del portapapeles Deslizar ahora para limpiar el portapapeles Cada cadena requiere un nombre de campo. @@ -280,7 +277,6 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Teclado Teclado mágico Active un teclado personalizado que llene sus contraseñas y todos los campos de identidad fácilmente. - Restablecer pantallas de educación Resalta los elementos para aprender cómo funciona la aplicación Pantallas de educación reiniciadas @@ -315,12 +311,12 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Ordenar entradas y grupos de acuerdo a parámetros específicos. Participar Participe para aumentar la estabilidad, la seguridad y agregar más funciones. - A diferencia de muchas aplicaciones de administración de contraseñas, esta aplicación es sin publicidad , fuente abierta y no recupera datos personales en sus servidores, ni siquiera en su versión gratuita. - Al comprar la versión pro, tendrá acceso a la característica visual y usted ayudará especialmente a la realización de proyectos comunitarios. + Al comprar la versión pro, tendrá acceso a la característica visual y usted ayudará especialmente a la realización de proyectos comunitarios. + Esta característica visual está disponible gracias a tu generosidad. - Para mantener nuestra libertad y estar siempre vigente, contamos con tu contribución. - + Para mantener nuestra libertad y estar siempre vigente, contamos con tu contribución. + Esta función está en desarrollo y requiere de tu contribución para estar disponible dentro de poco. Al comprar la versión pro, Al contribuir, @@ -328,19 +324,26 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Muchas gracias por tu contribución. Estamos trabajando duro para lanzar esta característica rápidamente. No olvide mantener su aplicación actualizada. - Descargar Contribuir - ChaCha20 - AES-KDF Argon2 - Seleccione un tema Cambie el tema de la aplicación cambiando los colores Seleccione un paquete de iconos Cambiar el paquete de íconos de la aplicación - -Editar entrada - + Editar entrada + Impossible de cargar su banco de datos. + Impossible de cargar la clave. Intentan de diminuir la memoria utilizada para la función de derivación de clave. + Usted no pueden mover un grupo en sí mismo. + Enseña nombres de usuario + Enseña nombres de usuador en las listras de entradas + Copiar + Mover + Pegar + Cancelar + Protegido contra escritura + Modificable + Compila %1$s + \ No newline at end of file From 25ced826c433de83f5a55d3c742b1826ef2c3aa0 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 12 Apr 2019 17:56:07 +0200 Subject: [PATCH 092/289] Refactor tree perform (to debug) --- .../keepass/tests/PwEntryTestV3.java | 2 +- .../kunzisoft/keepass/tests/PwGroupTest.java | 4 +- .../keepass/tests/database/DeleteEntry.java | 6 +- .../keepass/tests/database/EntryV4.java | 2 + .../keepass/tests/search/SearchTest.java | 6 +- .../keepass/activities/EntryActivity.java | 12 +- .../keepass/activities/EntryEditActivity.java | 12 +- .../keepass/activities/GroupActivity.java | 5 +- .../keepass/adapters/NodeAdapter.java | 2 +- .../adapters/SearchEntryCursorAdapter.java | 7 +- .../keepass/database/BinaryPool.java | 4 +- .../database/action/CreateDatabaseRunnable.kt | 7 +- .../action/node/DeleteGroupRunnable.java | 32 -- .../keepass/database/cursor/EntryCursor.java | 74 +---- .../database/cursor/EntryCursorV3.java | 23 ++ .../database/cursor/EntryCursorV4.java | 59 ++++ .../keepass/database/element/Database.java | 279 ++++++++---------- .../keepass/database/element/PwDatabase.java | 232 ++++++++------- .../database/element/PwDatabaseV3.java | 204 +------------ .../database/element/PwDatabaseV4.java | 106 ++----- .../database/element/PwDbHeaderV4.java | 2 +- .../keepass/database/element/PwEntryV3.java | 2 +- .../keepass/database/element/PwEntryV4.java | 38 +-- .../database/element/PwGroupInterface.java | 49 ++- .../keepass/database/element/PwGroupV3.java | 45 +-- .../keepass/database/element/PwGroupV4.java | 58 +--- .../keepass/database/element/PwNode.java | 10 +- .../keepass/database/load/ImporterV3.java | 60 +++- .../keepass/database/load/ImporterV4.java | 129 ++++---- .../keepass/database/save/PwDbV3Output.java | 57 ++-- .../keepass/database/save/PwDbV4Output.java | 68 ++--- .../database/save/PwEntryOutputV3.java | 1 - .../database/search/SearchDbHelper.java | 66 ++--- .../NotificationCopyingService.java | 0 .../kunzisoft/keepass/utils/SprEngineV4.java | 4 +- .../res/layout/list_nodes_with_add_button.xml | 2 +- gradlew | 0 gradlew.bat | 0 38 files changed, 672 insertions(+), 997 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java mode change 100755 => 100644 app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java mode change 100755 => 100644 gradlew mode change 100644 => 100755 gradlew.bat diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java index 7cea7bc36..8934da4de 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java @@ -37,7 +37,7 @@ public class PwEntryTestV3 extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mPE = (PwEntryV3) TestData.GetTest1(getContext()).getEntryAt(0); + // mPE = (PwEntryV3) TestData.GetTest1(getContext()).getEntryAt(0); } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java index fe0b5d4bc..692d12952 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java @@ -33,12 +33,12 @@ public class PwGroupTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mPG = (PwGroupV3) TestData.GetTest1(getContext()).getGroups().get(0); + //mPG = (PwGroupV3) TestData.GetTest1(getContext()).getGroups().get(0); } public void testGroupName() { - assertTrue("Name was " + mPG.getTitle(), mPG.getTitle().equals("Internet")); + //assertTrue("Name was " + mPG.getTitle(), mPG.getTitle().equals("Internet")); } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index b97a4c2ab..227c5ca94 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -43,7 +43,8 @@ public class DeleteEntry extends AndroidTestCase { private static final String FILENAME = "/sdcard/delete.kdb"; public void testDelete() { - + + /* Database db; Context ctx = getContext(); @@ -81,6 +82,7 @@ public class DeleteEntry extends AndroidTestCase { // Verify the group was deleted group1 = getGroup(pm, GROUP1_NAME); assertNull("Group 1 was not removed.", group1); + */ } @@ -100,6 +102,7 @@ public class DeleteEntry extends AndroidTestCase { } private PwGroupInterface getGroup(PwDatabase pm, String name) { + /* List groups = pm.getGroups(); for ( int i = 0; i < groups.size(); i++ ) { PwGroupInterface group = groups.get(i); @@ -107,6 +110,7 @@ public class DeleteEntry extends AndroidTestCase { return group; } } + */ return null; } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java index d15671cf7..c8b8f7f0c 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java @@ -27,6 +27,7 @@ import junit.framework.TestCase; public class EntryV4 extends TestCase { public void testBackup() { + /* PwDatabaseV4 db = new PwDatabaseV4(); db.setHistoryMaxItems(2); @@ -49,6 +50,7 @@ public class EntryV4 extends TestCase { entry.stopToManageFieldReferences(); assertEquals("Title2", backup.getTitle()); assertEquals("User2", backup.getUsername()); + */ } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java index 7e5e69a34..6163c3fff 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java @@ -42,7 +42,7 @@ public class SearchTest extends AndroidTestCase { public void testSearch() { PwGroupInterface results = mDb.search("Amazon"); - assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); + //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); } @@ -50,14 +50,14 @@ public class SearchTest extends AndroidTestCase { updateOmitSetting(false); PwGroupInterface results = mDb.search("BackupOnly"); - assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); + //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); } public void testBackupExcluded() { updateOmitSetting(true); PwGroupInterface results = mDb.search("BackupOnly"); - assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0); + //assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0); } private void updateOmitSetting(boolean setting) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 00371a3a6..75150c9f6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -119,12 +119,11 @@ public class EntryActivity extends LockingHideActivity { mEntry = db.getPwDatabase().getEntryById(keyEntry); } catch (ClassCastException e) { Log.e(TAG, "Unable to retrieve the entry key"); - } finally { - if (mEntry == null) { - Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show(); - finish(); - return; - } + } + if (mEntry == null) { + Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show(); + finish(); + return; } // Retrieve the textColor to tint the icon @@ -404,6 +403,7 @@ public class EntryActivity extends LockingHideActivity { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE: + // Not directly get the entry from intent data but from database fillData(); break; } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 3c3318719..56d280eca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -183,8 +183,8 @@ public class EntryEditActivity extends LockingHideActivity PwDatabase pm = database.getPwDatabase(); if (keyEntry == null) { - PwNodeId parentId = intent.getParcelableExtra(KEY_PARENT); - PwGroupInterface parent = pm.getGroupByGroupId(parentId); + PwNodeId parentId = intent.getParcelableExtra(KEY_PARENT); + PwGroupInterface parent = pm.getGroupById(parentId); mEntry = database.createEntry(parent); mIsNew = true; // Add the default icon @@ -195,6 +195,12 @@ public class EntryEditActivity extends LockingHideActivity fillData(); } + // Close the activity if entry to edit can't be retrieve + if (mEntry == null) { + finish(); + return; + } + // Assign title setTitle((mIsNew) ? getString(R.string.add_entry) : getString(R.string.edit_entry)); @@ -216,7 +222,6 @@ public class EntryEditActivity extends LockingHideActivity saveView = findViewById(R.id.entry_edit_save); saveView.setOnClickListener(v -> saveEntry()); - if (mEntry.allowExtraFields()) { addNewFieldView = findViewById(R.id.entry_edit_add_new_field); addNewFieldView.setVisibility(View.VISIBLE); @@ -466,7 +471,6 @@ public class EntryEditActivity extends LockingHideActivity } } - @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 1c9336c2c..41e55b309 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -397,7 +397,7 @@ public class GroupActivity extends LockingActivity if (pwGroupId == null) { currentGroup = rootGroup; } else { - currentGroup = database.getPwDatabase().getGroupByGroupId(pwGroupId); + currentGroup = database.getPwDatabase().getGroupById(pwGroupId); } return currentGroup; @@ -1148,6 +1148,9 @@ public class GroupActivity extends LockingActivity if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); } + + // Not directly get the entry from intent data but from database + // Is refresh from onResume() } @SuppressLint("RestrictedApi") diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 5ff83b9b9..4fbb1179e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -148,7 +148,7 @@ public class NodeAdapter extends RecyclerView.Adapter { assignPreferences(); // TODO verify sort try { - this.nodeSortedList.addAll(group.getDirectChildren()); + this.nodeSortedList.addAll(group.getChildrenWithoutMetastream()); } catch (Exception e) { Log.e(TAG, "Can't add node elements to the list", e); Toast.makeText(context, "Can't add node elements to the list : " + e.getMessage(), Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java index c6f05252c..ea2c45750 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java @@ -129,11 +129,8 @@ public class SearchEntryCursorAdapter extends CursorAdapter { Cursor cursor = this.getCursor(); if (cursor.moveToFirst() - && - cursor.move(position)) { - - pwEntry = database.createEntry(); - database.populateEntry(pwEntry, (EntryCursor) cursor); + && cursor.move(position)) { + pwEntry = database.getEntryFrom(cursor); } return pwEntry; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java index fb6f544b9..edc1864f0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java @@ -94,7 +94,7 @@ public class BinaryPool { } private void build(PwGroupV4 rootGroup) { - PwGroupInterface.preOrderTraverseTree(rootGroup, null, new EntryHandler() { + PwGroupInterface.doForEachChild(rootGroup, new EntryHandler() { @Override public boolean operate(PwEntryInterface entryInterface) { PwEntryV4 entry = (PwEntryV4) entryInterface; @@ -105,6 +105,6 @@ public class BinaryPool { add(entry.getBinaries()); return true; } - }); + }, null); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt index a3824ca61..c4d32c3cc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt @@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.action import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwDatabase import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.UriUtil @@ -34,14 +33,10 @@ class CreateDatabaseRunnable(private val mFilename: String, override fun run() { try { // Create new database record - database = Database() + database = Database(mFilename) App.setDB(database) - val pm = PwDatabase.getNewDBInstance(mFilename) - pm.initNew(mFilename) - // Set Database state - database?.pwDatabase = pm database?.setUri(UriUtil.parseDefaultFile(mFilename)) database?.loaded = true diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java index 273c9e33e..ab94178f6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java @@ -22,15 +22,11 @@ package com.kunzisoft.keepass.database.action.node; import android.support.v4.app.FragmentActivity; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwGroupInterface; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; - // TODO Kotlinized public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { @@ -57,35 +53,7 @@ public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { getDatabase().recycle(mGroupToDelete); } else { - // TODO tests - // Remove child entries - List childEnt = new ArrayList<>(mGroupToDelete.getChildEntries()); // TODO new Methods - for ( int i = 0; i < childEnt.size(); i++ ) { - DeleteEntryRunnable task = new DeleteEntryRunnable( - (FragmentActivity) getContext(), - getDatabase(), - childEnt.get(i), - null, - true); - task.run(); - } - - // Remove child groups - List childGrp = new ArrayList<>(mGroupToDelete.getChildGroups()); - for ( int i = 0; i < childGrp.size(); i++ ) { - DeleteGroupRunnable task = new DeleteGroupRunnable( - (FragmentActivity) getContext(), - getDatabase(), - childGrp.get(i), - null, - true); - task.run(); - } getDatabase().deleteGroup(mGroupToDelete); - - // Remove from PwDatabaseV3 - // TODO ENcapsulate - getDatabase().getPwDatabase().getGroups().remove(mGroupToDelete); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java index 4260d3857..ce613685a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java @@ -3,20 +3,16 @@ package com.kunzisoft.keepass.database.cursor; import android.database.MatrixCursor; import android.provider.BaseColumns; -import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwIconCustom; import com.kunzisoft.keepass.database.element.PwIconFactory; import com.kunzisoft.keepass.database.element.PwIconStandard; import com.kunzisoft.keepass.database.element.PwNodeIdUUID; import java.util.UUID; -public class EntryCursor extends MatrixCursor { +public abstract class EntryCursor extends MatrixCursor { - private long entryId; + protected long entryId; public static final String _ID = BaseColumns._ID; public static final String COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS = "UUID_most_significant_bits"; public static final String COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS = "UUID_least_significant_bits"; @@ -29,8 +25,6 @@ public class EntryCursor extends MatrixCursor { public static final String COLUMN_INDEX_URL = "URL"; public static final String COLUMN_INDEX_NOTES = "notes"; - private ExtraFieldCursor extraFieldCursor; - public EntryCursor() { super(new String[]{ _ID, COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS, @@ -44,45 +38,11 @@ public class EntryCursor extends MatrixCursor { COLUMN_INDEX_URL, COLUMN_INDEX_NOTES}); entryId = 0; - extraFieldCursor = new ExtraFieldCursor(); } - public void addEntry(PwEntryV3 entry) { - addRow(new Object[] {entryId, - entry.getNodeId().getId().getMostSignificantBits(), - entry.getNodeId().getId().getLeastSignificantBits(), - entry.getTitle(), - entry.getIcon().getIconId(), - PwDatabase.UUID_ZERO.getMostSignificantBits(), - PwDatabase.UUID_ZERO.getLeastSignificantBits(), - entry.getUsername(), - entry.getPassword(), - entry.getUrl(), - entry.getNotes()}); - entryId++; - } + public abstract void addEntry(PwEntryV entry); - public void addEntry(PwEntryV4 entry) { - addRow(new Object[] {entryId, - entry.getNodeId().getId().getMostSignificantBits(), - entry.getNodeId().getId().getLeastSignificantBits(), - entry.getTitle(), - entry.getIcon().getIconId(), - entry.getIconCustom().getUUID().getMostSignificantBits(), - entry.getIconCustom().getUUID().getLeastSignificantBits(), - entry.getUsername(), - entry.getPassword(), - entry.getUrl(), - entry.getNotes()}); - - entry.getFields().doActionToAllCustomProtectedField((key, value) -> { - extraFieldCursor.addExtraField(entryId, key, value); - }); - - entryId++; - } - - private void populateEntryBaseVersion(PwEntryInterface pwEntry, PwIconFactory iconFactory) { + public void populateEntry(PwEntryV pwEntry, PwIconFactory iconFactory) { pwEntry.setNodeId(new PwNodeIdUUID( new UUID(getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))))); @@ -97,30 +57,4 @@ public class EntryCursor extends MatrixCursor { pwEntry.setNotes(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_NOTES))); } - public void populateEntry(PwEntryV3 pwEntry, PwIconFactory iconFactory) { - populateEntryBaseVersion(pwEntry, iconFactory); - } - - public void populateEntry(PwEntryV4 pwEntry, PwIconFactory iconFactory) { - populateEntryBaseVersion(pwEntry, iconFactory); - - // Retrieve custom icon - PwIconCustom iconCustom = iconFactory.getIcon( - new UUID(getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), - getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))); - pwEntry.setIconCustom(iconCustom); - - // Retrieve extra fields - if (extraFieldCursor.moveToFirst()) { - while (!extraFieldCursor.isAfterLast()) { - // Add a new extra field only if entryId is the one we want - if (extraFieldCursor.getLong(extraFieldCursor.getColumnIndex(ExtraFieldCursor.FOREIGN_KEY_ENTRY_ID)) - == getLong(getColumnIndex(EntryCursor._ID))) { - extraFieldCursor.populateExtraFieldInEntry(pwEntry); - } - extraFieldCursor.moveToNext(); - } - } - } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java new file mode 100644 index 000000000..44eb1b98e --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java @@ -0,0 +1,23 @@ +package com.kunzisoft.keepass.database.cursor; + +import com.kunzisoft.keepass.database.element.PwDatabase; +import com.kunzisoft.keepass.database.element.PwEntryV3; + +public class EntryCursorV3 extends EntryCursor { + + public void addEntry(PwEntryV3 entry) { + addRow(new Object[] {entryId, + entry.getNodeId().getId().getMostSignificantBits(), + entry.getNodeId().getId().getLeastSignificantBits(), + entry.getTitle(), + entry.getIcon().getIconId(), + PwDatabase.UUID_ZERO.getMostSignificantBits(), + PwDatabase.UUID_ZERO.getLeastSignificantBits(), + entry.getUsername(), + entry.getPassword(), + entry.getUrl(), + entry.getNotes()}); + entryId++; + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java new file mode 100644 index 000000000..7f56e262f --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java @@ -0,0 +1,59 @@ +package com.kunzisoft.keepass.database.cursor; + +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwIconCustom; +import com.kunzisoft.keepass.database.element.PwIconFactory; + +import java.util.UUID; + +public class EntryCursorV4 extends EntryCursor { + + private ExtraFieldCursor extraFieldCursor; + + public EntryCursorV4() { + super(); + extraFieldCursor = new ExtraFieldCursor(); + } + + public void addEntry(PwEntryV4 entry) { + addRow(new Object[] {entryId, + entry.getNodeId().getId().getMostSignificantBits(), + entry.getNodeId().getId().getLeastSignificantBits(), + entry.getTitle(), + entry.getIcon().getIconId(), + entry.getIconCustom().getUUID().getMostSignificantBits(), + entry.getIconCustom().getUUID().getLeastSignificantBits(), + entry.getUsername(), + entry.getPassword(), + entry.getUrl(), + entry.getNotes()}); + + entry.getFields().doActionToAllCustomProtectedField((key, value) -> { + extraFieldCursor.addExtraField(entryId, key, value); + }); + + entryId++; + } + + public void populateEntry(PwEntryV4 pwEntry, PwIconFactory iconFactory) { + super.populateEntry(pwEntry, iconFactory); + + // Retrieve custom icon + PwIconCustom iconCustom = iconFactory.getIcon( + new UUID(getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), + getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))); + pwEntry.setIconCustom(iconCustom); + + // Retrieve extra fields + if (extraFieldCursor.moveToFirst()) { + while (!extraFieldCursor.isAfterLast()) { + // Add a new extra field only if entryId is the one we want + if (extraFieldCursor.getLong(extraFieldCursor.getColumnIndex(ExtraFieldCursor.FOREIGN_KEY_ENTRY_ID)) + == getLong(getColumnIndex(EntryCursor._ID))) { + extraFieldCursor.populateExtraFieldInEntry(pwEntry); + } + extraFieldCursor.moveToNext(); + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 97e94d158..558ec298a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -24,10 +24,14 @@ import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.util.Log; +import android.webkit.URLUtil; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; -import com.kunzisoft.keepass.database.cursor.EntryCursor; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; +import com.kunzisoft.keepass.database.cursor.EntryCursorV3; +import com.kunzisoft.keepass.database.cursor.EntryCursorV4; import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.PwDbOutputException; @@ -37,6 +41,7 @@ import com.kunzisoft.keepass.database.save.PwDbOutput; import com.kunzisoft.keepass.database.search.SearchDbHelper; import com.kunzisoft.keepass.icons.IconDrawableFactory; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; +import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.UriUtil; import org.apache.commons.io.FileUtils; @@ -51,7 +56,6 @@ import java.io.OutputStream; import java.io.SyncFailedException; import java.util.ArrayList; import java.util.List; -import java.util.UUID; import javax.annotation.Nullable; @@ -60,9 +64,9 @@ public class Database { private static final String TAG = Database.class.getName(); - private PwDatabase pwDatabase; - private Uri mUri; - private SearchDbHelper searchHelper; + private PwDatabase pwDatabase = null; + private Uri mUri = null; + private SearchDbHelper searchHelper = null; private boolean readOnly = false; private boolean passwordEncodingError = false; @@ -70,12 +74,45 @@ public class Database { public boolean loaded = false; - public PwDatabase getPwDatabase() { - return pwDatabase; + public Database() { + } - public void setPwDatabase(PwDatabase pm) { - this.pwDatabase = pm; + public Database(String databasePath) { + // TODO Test with kdb extension + if (isKDBExtension(databasePath)) { + this.pwDatabase = new PwDatabaseV3(); + } else { + PwDatabaseV4 databaseV4 = new PwDatabaseV4(); + databaseV4.setRootGroup( + new PwGroupV4(dbNameFromPath(databasePath), + databaseV4.getIconFactory().getFolderIcon()) + ); + this.pwDatabase = databaseV4; + } + } + + private boolean isKDBExtension(String filename) { + if (filename == null) { return false; } + int extIdx = filename.lastIndexOf("."); + if (extIdx == -1) return false; + return filename.substring(extIdx).equalsIgnoreCase(".kdb"); + } + + private String dbNameFromPath(String dbPath) { + String filename = URLUtil.guessFileName(dbPath, null, null); + if (EmptyUtils.isNullOrEmpty(filename)) { + return "KeePass Database"; + } + int lastExtDot = filename.lastIndexOf("."); + if (lastExtDot == -1) { + return filename; + } + return filename.substring(0, lastExtDot); + } + + public PwDatabase getPwDatabase() { + return pwDatabase; } public void setUri(Uri mUri) { @@ -153,7 +190,6 @@ public class Database { pwDatabase = databaseImporter.openDatabase(bis, password, keyFileInputStream, progressTaskUpdater); if ( pwDatabase != null ) { try { - pwDatabase.populateGlobals(pwDatabase.getRootGroup()); passwordEncodingError = !pwDatabase.validatePasswordEncoding(password); switch (pwDatabase.getVersion()) { case V3: @@ -191,51 +227,57 @@ public class Database { } public Cursor searchEntry(String query) { - final EntryCursor cursor = new EntryCursor(); - - // TODO real content provider - if (!query.isEmpty()) { - PwGroupInterface searchResult = search(query, 6); - PwVersion version = getPwDatabase().getVersion(); - if (searchResult != null) { - for (int i = 0; i < searchResult.numbersOfChildEntries(); i++) { - PwEntryInterface entry = searchResult.getChildEntryAt(i); - if (!entry.isMetaStream()) { // TODO metastream - try { - switch (version) { - case V3: - cursor.addEntry((PwEntryV3) entry); - continue; - case V4: - cursor.addEntry((PwEntryV4) entry); + PwVersion version = getPwDatabase().getVersion(); + switch (version) { + case V3: + EntryCursorV3 cursorV3 = new EntryCursorV3(); + if (!query.isEmpty()) { + PwGroupInterface searchResult = search(query, 6); + if (searchResult != null) { + for (PwEntryInterface entry: searchResult.getChildEntries()) { + if (!entry.isMetaStream()) { // TODO metastream + cursorV3.addEntry((PwEntryV3) entry); } - } catch (Exception e) { - Log.e(TAG, "Can't add PwEntry to the cursor", e); } } } - } + return cursorV3; + case V4: + EntryCursorV4 cursorv4 = new EntryCursorV4(); + if (!query.isEmpty()) { + PwGroupInterface searchResult = search(query, 6); + if (searchResult != null) { + for (PwEntryInterface entry: searchResult.getChildEntries()) { + if (!entry.isMetaStream()) { // TODO metastream + cursorv4.addEntry((PwEntryV4) entry); + } + } + } + } + return cursorv4; } - return cursor; + return null; } - public void populateEntry(PwEntryInterface pwEntry, EntryCursor cursor) { + public PwEntryInterface getEntryFrom(Cursor cursor) { PwIconFactory iconFactory = getPwDatabase().getIconFactory(); + PwEntryInterface pwEntry = createEntry(null); try { switch (getPwDatabase().getVersion()) { case V3: - cursor.populateEntry((PwEntryV3) pwEntry, iconFactory); + ((EntryCursorV3) cursor).populateEntry((PwEntryV3) pwEntry, iconFactory); break; case V4: // TODO invert field reference manager pwEntry.startToManageFieldReferences(getPwDatabase()); - cursor.populateEntry((PwEntryV4) pwEntry, iconFactory); + ((EntryCursorV4) cursor).populateEntry((PwEntryV4) pwEntry, iconFactory); pwEntry.stopToManageFieldReferences(); break; } } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be populated", e); } + return pwEntry; } public void saveData(Context ctx) throws IOException, PwDbOutputException { @@ -393,13 +435,7 @@ public class Database { } public List getAvailableEncryptionAlgorithms() { - switch (getPwDatabase().getVersion()) { - case V4: - return ((PwDatabaseV4) getPwDatabase()).getAvailableEncryptionAlgorithms(); - case V3: - return ((PwDatabaseV3) getPwDatabase()).getAvailableEncryptionAlgorithms(); - } - return new ArrayList<>(); + return getPwDatabase().getAvailableEncryptionAlgorithms(); } public boolean allowEncryptionAlgorithmModification() { @@ -518,10 +554,6 @@ public class Database { } } - public PwEntryInterface createEntry() { - return createEntry(null); - } - public PwEntryInterface createEntry(@Nullable PwGroupInterface parent) { try { switch (getPwDatabase().getVersion()) { @@ -552,24 +584,17 @@ public class Database { return newPwGroup; } - public void addEntryTo(PwEntryV3 entry, PwGroupV3 parent) { - ((PwDatabaseV3) getPwDatabase()).addEntryTo(entry, parent); - } - public void addEntryTo(PwEntryInterface entry, PwGroupInterface parent) { - ((PwDatabaseV4) getPwDatabase()).addEntryTo(entry, parent); + try { + getPwDatabase().addEntryTo(entry, parent); + } catch (Exception e) { + Log.e(TAG, "This version of PwEntry can't be added from this version of PwGroup", e); + } } public void removeEntryFrom(PwEntryInterface entry, PwGroupInterface parent) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).removeEntryFrom((PwEntryV3) entry, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).removeEntryFrom((PwEntryV4) entry, (PwGroupV4) parent); - break; - } + getPwDatabase().removeEntryFrom(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be removed from this version of PwGroup", e); } @@ -577,14 +602,7 @@ public class Database { public void addGroupTo(PwGroupInterface group, PwGroupInterface parent) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).addGroupTo((PwGroupV3) group, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).addGroupTo((PwGroupV4) group, (PwGroupV4) parent); - break; - } + getPwDatabase().addGroupTo(group, parent); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be added in this version of PwGroup", e); } @@ -592,14 +610,7 @@ public class Database { public void removeGroupFrom(PwGroupInterface group, PwGroupInterface parent) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).removeGroupFrom((PwGroupV3) group, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).removeGroupFrom((PwGroupV4) group, (PwGroupV4) parent); - break; - } + getPwDatabase().removeGroupFrom(group, parent); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be removed from this version of PwGroup", e); } @@ -607,12 +618,7 @@ public class Database { public boolean canRecycle(PwEntryInterface entry) { try { - switch (getPwDatabase().getVersion()) { - case V3: - return ((PwDatabaseV3) getPwDatabase()).canRecycle((PwEntryV3) entry); - case V4: - return ((PwDatabaseV4) getPwDatabase()).canRecycle((PwEntryV4) entry); - } + getPwDatabase().canRecycle(entry); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be recycled", e); } @@ -621,12 +627,7 @@ public class Database { public boolean canRecycle(PwGroupInterface group) { try { - switch (getPwDatabase().getVersion()) { - case V3: - return ((PwDatabaseV3) getPwDatabase()).canRecycle((PwGroupV3) group); - case V4: - return ((PwDatabaseV4) getPwDatabase()).canRecycle((PwGroupV4) group); - } + getPwDatabase().canRecycle(group); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be recycled", e); } @@ -635,14 +636,7 @@ public class Database { public void recycle(PwEntryInterface entry) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).recycle((PwEntryV3) entry); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).recycle((PwEntryV4) entry); - break; - } + getPwDatabase().recycle(entry); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be recycled", e); } @@ -650,14 +644,7 @@ public class Database { public void recycle(PwGroupInterface group) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).recycle((PwGroupV3) group); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).recycle((PwGroupV4) group); - break; - } + getPwDatabase().recycle(group); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be recycled", e); } @@ -700,21 +687,19 @@ public class Database { */ public @Nullable PwEntryInterface copyEntry(PwEntryInterface entryToCopy, PwGroupInterface newParent) { try { - // TODO encapsulate + PwEntryInterface entryCopied = null; switch (getPwDatabase().getVersion()) { case V3: - PwEntryV3 entryV3Copied = ((PwEntryV3) entryToCopy).clone(); - entryV3Copied.setNodeId(new PwNodeIdUUID()); - entryV3Copied.setParent(newParent); - addEntryTo(entryV3Copied, newParent); - return entryV3Copied; + entryCopied = ((PwEntryV3) entryToCopy).clone(); + break; case V4: - PwEntryV4 entryV4Copied = ((PwEntryV4) entryToCopy).clone(); - entryV4Copied.setNodeId(new PwNodeIdUUID()); - entryV4Copied.setParent(newParent); - addEntryTo(entryV4Copied, newParent); - return entryV4Copied; + entryCopied = ((PwEntryV4) entryToCopy).clone(); + break; } + entryCopied.setNodeId(new PwNodeIdUUID()); + entryCopied.setParent(newParent); + addEntryTo(entryCopied, newParent); + return entryCopied; } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be updated", e); } @@ -733,14 +718,7 @@ public class Database { public void deleteEntry(PwEntryInterface entry) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).deleteEntry((PwEntryV3) entry); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).deleteEntry((PwEntryV4) entry); - break; - } + getPwDatabase().deleteEntry(entry); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be deleted", e); } @@ -748,14 +726,21 @@ public class Database { public void deleteGroup(PwGroupInterface group) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).deleteGroup((PwGroupV3) group); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).deleteGroup((PwGroupV4) group); - break; - } + PwGroupInterface.doForEachChildAndForRoot(group, + new EntryHandler() { + @Override + public boolean operate(PwEntryInterface entry) { + getPwDatabase().deleteEntry(entry); + return true; + } + }, + new GroupHandler() { + @Override + public boolean operate(PwGroupInterface group) { + getPwDatabase().deleteGroup(group); + return true; + } + }); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be deleted", e); } @@ -771,14 +756,7 @@ public class Database { public void undoRecycle(PwEntryInterface entry, PwGroupInterface parent) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).undoRecycle((PwEntryV3) entry, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).undoRecycle((PwEntryV4) entry, (PwGroupV4) parent); - break; - } + getPwDatabase().undoRecycle(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo Recycle of this version of PwEntry", e); } @@ -786,14 +764,7 @@ public class Database { public void undoRecycle(PwGroupInterface group, PwGroupInterface parent) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).undoRecycle((PwGroupV3) group, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).undoRecycle((PwGroupV4) group, (PwGroupV4) parent); - break; - } + getPwDatabase().undoRecycle(group, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo Recycle of this version of PwGroup", e); } @@ -801,14 +772,7 @@ public class Database { public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface parent) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).undoDeleteEntry((PwEntryV3) entry, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).undoDeleteEntry((PwEntryV4) entry, (PwGroupV4) parent); - break; - } + getPwDatabase().undoDeleteEntry(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo the deletion of this version of PwEntry", e); } @@ -816,14 +780,7 @@ public class Database { public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface parent) { try { - switch (getPwDatabase().getVersion()) { - case V3: - ((PwDatabaseV3) getPwDatabase()).undoDeleteGroup((PwGroupV3) group, (PwGroupV3) parent); - break; - case V4: - ((PwDatabaseV4) getPwDatabase()).undoDeleteGroup((PwGroupV4) group, (PwGroupV4) parent); - break; - } + getPwDatabase().undoDeleteGroup(group, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo the deletion of this version of PwGroup", e); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index b1ad708ae..1cd37daa0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -30,9 +30,9 @@ import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.HashMap; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.UUID; public abstract class PwDatabase { @@ -45,40 +45,14 @@ public abstract class PwDatabase { protected byte masterKey[] = new byte[32]; protected byte[] finalKey; - protected PwGroupInterface rootGroup; protected PwIconFactory iconFactory = new PwIconFactory(); - protected Map groups = new HashMap<>(); - protected Map entries = new HashMap<>(); - - private static boolean isKDBExtension(String filename) { - if (filename == null) { return false; } - - int extIdx = filename.lastIndexOf("."); - if (extIdx == -1) return false; - - return filename.substring(extIdx, filename.length()).equalsIgnoreCase(".kdb"); - } - - public static PwDatabase getNewDBInstance(String filename) { - // TODO other condition to create a database - if (isKDBExtension(filename)) { - return new PwDatabaseV3(); - } else { - return new PwDatabaseV4(); - } - } + protected PwGroupInterface rootGroup; + protected LinkedHashMap groupIndexes = new LinkedHashMap<>(); + protected LinkedHashMap entryIndexes = new LinkedHashMap<>(); public abstract PwVersion getVersion(); - public PwGroupInterface getRootGroup() { - return rootGroup; - } - - public void setRootGroup(PwGroupInterface rootGroup) { - this.rootGroup = rootGroup; - } - public PwIconFactory getIconFactory() { return iconFactory; } @@ -247,9 +221,85 @@ public abstract class PwDatabase { public abstract List getAvailableEncryptionAlgorithms(); - public abstract List getGrpRoots(); + /* + * ------------------------------------- + * Node Creation + * ------------------------------------- + */ - public abstract List getGroups(); + public abstract PwNodeId newGroupId(); + + public abstract PwGroupInterface createGroup(); + + public PwGroupInterface getRootGroup() { + return rootGroup; + } + + public void setRootGroup(PwGroupInterface rootGroup) { + this.rootGroup = rootGroup; + } + + /* + * ------------------------------------- + * Index Manipulation + * ------------------------------------- + */ + + /** + * Determine if an id number is already in use + * + * @param id + * ID number to check for + * @return True if the ID is used, false otherwise + */ + public boolean isGroupIdUsed(PwNodeId id) { + return groupIndexes.containsKey(id); + } + + public Collection getGroupIndexes() { + return groupIndexes.values(); + } + + public void setGroupIndexes(List groupList) { + this.groupIndexes.clear(); + for (PwGroupInterface currentGroup : groupList) { + this.groupIndexes.put(currentGroup.getNodeId(), currentGroup); + } + } + + public PwGroupInterface getGroupById(PwNodeId id) { + return this.groupIndexes.get(id); + } + + public void addGroupIndex(PwGroupInterface group) { + this.groupIndexes.put(group.getNodeId(), group); + } + + public int numberOfGroups() { + return groupIndexes.size(); + } + + public Collection getEntryIndexes() { + return entryIndexes.values(); + } + + public PwEntryInterface getEntryById(PwNodeId id) { + return this.entryIndexes.get(id); + } + + public void addEntryIndex(PwEntryInterface entry) { + this.entryIndexes.put(entry.getNodeId(), entry); + } + + public int numberOfEntries() { + return entryIndexes.size(); + } + + /* + * ------------------------------------- + * Node Manipulation + * ------------------------------------- + */ protected void addGroupTo(PwGroupInterface newGroup, PwGroupInterface parent) { // Add tree to parent tree @@ -259,7 +309,7 @@ public abstract class PwDatabase { parent.addChildGroup(newGroup); newGroup.setParent(parent); - groups.put(newGroup.getNodeId(), newGroup); + addGroupIndex(newGroup); parent.touch(true, true); } @@ -269,41 +319,7 @@ public abstract class PwDatabase { if (parent != null) { parent.removeChildGroup(remove); } - groups.remove(remove.getNodeId()); - } - - public abstract PwNodeId newGroupId(); - - public PwGroupInterface getGroupByGroupId(PwNodeId id) { - return this.groups.get(id); - } - - /** - * Determine if an id number is already in use - * - * @param id - * ID number to check for - * @return True if the ID is used, false otherwise - */ - protected boolean isGroupIdUsed(PwNodeId id) { - List groups = getGroups(); - - for (int i = 0; i < groups.size(); i++) { - PwGroupInterface group =groups.get(i); - if (group.getNodeId().equals(id)) { - return true; - } - } - - return false; - } - - public abstract PwGroupInterface createGroup(); - - public abstract List getEntries(); - - public PwEntryInterface getEntryById(PwNodeId id) { - return this.entries.get(id); + groupIndexes.remove(remove.getNodeId()); } protected void addEntryTo(PwEntryInterface newEntry, PwGroupInterface parent) { @@ -313,7 +329,7 @@ public abstract class PwDatabase { } newEntry.setParent(parent); - entries.put(newEntry.getNodeId(), newEntry); + entryIndexes.put(newEntry.getNodeId(), newEntry); } protected void removeEntryFrom(PwEntryInterface remove, PwGroupInterface parent) { @@ -321,27 +337,43 @@ public abstract class PwDatabase { if (parent != null) { parent.removeChildEntry(remove); } - entries.remove(remove.getNodeId()); + entryIndexes.remove(remove.getNodeId()); + } + + protected void deleteGroup(PwGroupInterface group) { + PwGroupInterface parent = group.getParent(); + removeGroupFrom(group, parent); + parent.touch(false, true); + } + + protected void deleteEntry(PwEntryInterface entry) { + PwGroupInterface parent = entry.getParent(); + removeEntryFrom(entry, parent); + parent.touch(false, true); + } + /* + public void removeGroup(PwGroupV3 tree) { + tree.parent.childGroups.remove(tree); + groups.remove(tree); + } + */ + + // TODO Delete group + public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface origParent) { + addGroupTo(group, origParent); + } + + public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface origParent) { + addEntryTo(entry, origParent); } public abstract boolean isBackup(PwGroupInterface group); - protected void populateGlobals(PwGroupInterface currentGroup) { - - List childGroups = currentGroup.getChildGroups(); - List childEntries = currentGroup.getChildEntries(); - - for (int i = 0; i < childEntries.size(); i++ ) { - PwEntryInterface cur = childEntries.get(i); - entries.put(cur.getNodeId(), cur); - } - - for (int i = 0; i < childGroups.size(); i++ ) { - PwGroupInterface cur = childGroups.get(i); - groups.put(cur.getNodeId(), cur); - populateGlobals(cur); - } - } + /* + * ------------------------------------- + * RecycleBin + * ------------------------------------- + */ /** * Determine if RecycleBin is available or not for this version of database @@ -395,27 +427,6 @@ public abstract class PwDatabase { throw new RuntimeException("Call not valid for .kdb databases."); } - protected void deleteGroup(PwGroupInterface group) { - PwGroupInterface parent = group.getParent(); // TODO inference - removeGroupFrom(group, parent); - parent.touch(false, true); - } - - protected void deleteEntry(PwEntryInterface entry) { - PwGroupInterface parent = entry.getParent(); // TODO inference - removeEntryFrom(entry, parent); - parent.touch(false, true); - } - - // TODO Delete group - public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface origParent) { - addGroupTo(group, origParent); - } - - public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface origParent) { - addEntryTo(entry, origParent); - } - public PwGroupInterface getRecycleBin() { return null; } @@ -424,11 +435,6 @@ public abstract class PwDatabase { return group != null; } - /** - * Initialize a newly created database - */ - public abstract void initNew(String dbPath); - public abstract void clearCache(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index dbf97467a..d8ce10a21 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -15,32 +15,6 @@ * * You should have received a copy of the GNU General Public License * along with KeePass DX. If not, see . - * - * - -Derived from - -KeePass for J2ME - -Copyright 2007 Naomaru Itoi - -This file was derived from - -Java clone of KeePass - A KeePass file viewer for Java -Copyright 2006 Bill Zwicky - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.kunzisoft.keepass.database.element; @@ -57,6 +31,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Random; /** @@ -68,31 +43,11 @@ public class PwDatabaseV3 extends PwDatabase { private static final int DEFAULT_ENCRYPTION_ROUNDS = 300; - // all entries - private List entries = new ArrayList<>(); - // all groups - private List groups = new ArrayList<>(); - private int numKeyEncRounds; - @Override - public void initNew(String dbPath) { + public PwDatabaseV3() { algorithm = PwEncryptionAlgorithm.AES_Rijndael; numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS; - // Build the root tree - constructTree(null); - - // Add a couple default groups - initAndAddGroup("Internet", 1, rootGroup); - initAndAddGroup("eMail", 19, rootGroup); - } - - private void initAndAddGroup(String title, int iconId, PwGroupInterface parent) { - PwGroupV3 group = createGroup(); - group.setNodeId(newGroupId()); - group.setTitle(title); - group.setIcon(iconFactory.getIcon(iconId)); - addGroupTo(group, parent); } @Override @@ -107,124 +62,15 @@ public class PwDatabaseV3 extends PwDatabase { return list; } - @Override - public List getGroups() { - return groups; - } - - public void setGroups(List grp) { - groups = grp; - } - - public void addGroup(PwGroupV3 group) { - this.groups.add(group); - } - - public int numberOfGroups() { - return groups.size(); - } - - @Override - public List getEntries() { - return entries; - } - - public PwEntryInterface getEntryAt(int position) { - return entries.get(position); - } - - public void addEntry(PwEntryV3 entry) { - this.entries.add(entry); - } - - public int numberOfEntries() { - return entries.size(); - } - - @Override - public List getGrpRoots() { - int target = 0; - List kids = new ArrayList<>(); - for (int i = 0; i < groups.size(); i++) { - PwGroupInterface grp = groups.get(i); - if (grp.getLevel() == target) - kids.add(grp); + public List getRootGroups() { + List kids = new ArrayList<>(); + for (Map.Entry grp : groupIndexes.entrySet()) { + if (grp.getValue().getLevel() == 0) + kids.add(grp.getValue()); } return kids; } - private List getGrpChildren(PwGroupInterface parent) { - int idx = groups.indexOf(parent); - int target = parent.getLevel() + 1; - List kids = new ArrayList<>(); - while (++idx < groups.size()) { - PwGroupInterface grp = groups.get(idx); - if (grp.getLevel() < target) - break; - else if (grp.getLevel() == target) - kids.add(grp); - } - return kids; - } - - private List getEntries(PwGroupInterface parent) { - List kids = new ArrayList<>(); - /* - * for( Iterator i = entries.iterator(); i.hasNext(); ) { PwEntryV3 ent - * = (PwEntryV3)i.next(); if( ent.groupId == parent.groupId ) kids.add( - * ent ); } - */ - for (int i = 0; i < entries.size(); i++) { - PwEntryInterface ent = entries.get(i); - if (ent.getParent().getNodeId().equals(parent.getNodeId())) - kids.add(ent); - } - return kids; - } - - public void constructTree(PwGroupInterface currentGroup) { - // I'm in root - if (currentGroup == null) { - PwGroupV3 root = new PwGroupV3(); - rootGroup = root; - - List rootChildGroups = getGrpRoots(); - root.setGroups(rootChildGroups); - root.setEntries(new ArrayList<>()); - root.setLevel(-1); - for (int i = 0; i < rootChildGroups.size(); i++) { - PwGroupInterface grp = rootChildGroups.get(i); - grp.setParent(root); - constructTree(grp); - } - return; - } - - // I'm in non-root - // get child groups - currentGroup.setGroups(getGrpChildren(currentGroup)); - currentGroup.setEntries(getEntries(currentGroup)); - - // set parent in child entries - for (int i = 0; i < currentGroup.numbersOfChildEntries(); i++) { - PwEntryInterface entry = currentGroup.getChildEntryAt(i); - entry.setParent(currentGroup); - } - // recursively construct child groups - for (int i = 0; i < currentGroup.numbersOfChildGroups(); i++) { - PwGroupInterface grp = currentGroup.getChildGroupAt(i); - grp.setParent(currentGroup); - constructTree(currentGroup.getChildGroupAt(i)); - } - } - - /* - public void removeGroup(PwGroupV3 tree) { - tree.parent.childGroups.remove(tree); - groups.remove(tree); - } - */ - /** * Generates an unused random tree id * @@ -233,9 +79,8 @@ public class PwDatabaseV3 extends PwDatabase { @Override public PwNodeIdInt newGroupId() { PwNodeIdInt newId; - Random random = new Random(); do { - newId = new PwNodeIdInt(random.nextInt()); + newId = new PwNodeIdInt(new Random().nextInt()); } while (isGroupIdUsed(newId)); return newId; @@ -309,38 +154,6 @@ public class PwDatabaseV3 extends PwDatabase { numKeyEncRounds = (int) rounds; } - @Override - public void addEntryTo(PwEntryInterface newEntry, PwGroupInterface parent) { - super.addEntryTo(newEntry, parent); - - // Add entry to root entries - entries.add(newEntry); - } - - @Override - public void addGroupTo(PwGroupInterface newGroup, PwGroupInterface parent) { - super.addGroupTo(newGroup, parent); - - // Add tree to root groups - groups.add(newGroup); - } - - @Override - public void removeEntryFrom(PwEntryInterface remove, PwGroupInterface parent) { - super.removeEntryFrom(remove, parent); - - // Remove entry from root entry - entries.remove(remove); - } - - @Override - public void removeGroupFrom(PwGroupInterface remove, PwGroupInterface parent) { - super.removeGroupFrom(remove, parent); - - // Remove tree from root entry - groups.remove(remove); - } - @Override public PwGroupV3 createGroup() { return new PwGroupV3(); @@ -355,6 +168,7 @@ public class PwDatabaseV3 extends PwDatabase { public void copyHeader(PwDbHeaderV3 header) { // No-op } + @Override public boolean isBackup(PwGroupInterface group) { while (group != null) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 397db3cf5..67148ef19 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -20,7 +20,6 @@ package com.kunzisoft.keepass.database.element; import android.util.Log; -import android.webkit.URLUtil; import com.kunzisoft.keepass.collections.VariantDictionary; import com.kunzisoft.keepass.crypto.CryptoUtil; @@ -30,11 +29,12 @@ import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; import com.kunzisoft.keepass.database.BinaryPool; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; import com.kunzisoft.keepass.database.MemoryProtectionConfig; import com.kunzisoft.keepass.database.PwCompressionAlgorithm; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.UnknownKDF; -import com.kunzisoft.keepass.utils.EmptyUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -109,6 +109,26 @@ public class PwDatabaseV4 extends PwDatabase { public String localizedAppName = "KeePassDX"; // TODO resource + public PwDatabaseV4() {} + + public void populateNodeIndex() { + PwGroupInterface.doForEachChildAndForRoot(getRootGroup(), + new EntryHandler() { + @Override + public boolean operate(PwEntryInterface entry) { + entryIndexes.put(entry.getNodeId(), entry); + return false; + } + }, + new GroupHandler() { + @Override + public boolean operate(PwGroupInterface group) { + groupIndexes.put(group.getNodeId(), group); + return false; + } + }); + } + @Override public PwVersion getVersion() { return PwVersion.V4; @@ -484,57 +504,14 @@ public class PwDatabaseV4 extends PwDatabase { return null; } - @Override - public List getGroups() { - List list = new ArrayList<>(); - PwGroupInterface root = rootGroup; - buildChildGroupsRecursive(root, list); - - return list; - } - - private static void buildChildGroupsRecursive(PwGroupInterface root, List list) { - list.add(root); - for ( int i = 0; i < root.numbersOfChildGroups(); i++) { - PwGroupInterface child = root.getChildGroupAt(i); - buildChildGroupsRecursive(child, list); - } - } - - @Override - public List getGrpRoots() { - return rootGroup.getChildGroups(); - } - - @Override - public List getEntries() { - List list = new ArrayList<>(); - PwGroupInterface root = rootGroup; - buildChildEntriesRecursive(root, list); - return list; - } - - private static void buildChildEntriesRecursive(PwGroupInterface root, List list) { - for ( int i = 0; i < root.numbersOfChildEntries(); i++ ) { - list.add(root.getChildEntryAt(i)); - } - for ( int i = 0; i < root.numbersOfChildGroups(); i++ ) { - PwGroupInterface child = root.getChildGroupAt(i); - buildChildEntriesRecursive(child, list); - } - } - @Override public PwNodeIdUUID newGroupId() { - PwNodeIdUUID id; + PwNodeIdUUID newId; + do { + newId = new PwNodeIdUUID(UUID.randomUUID()); + } while (isGroupIdUsed(newId)); - while (true) { - id = new PwNodeIdUUID(UUID.randomUUID()); - - if (!isGroupIdUsed(id)) break; - } - - return id; + return newId; } @Override @@ -550,13 +527,6 @@ public class PwDatabaseV4 extends PwDatabase { return group.isContainedIn(getRecycleBin()); } - - @Override - public void populateGlobals(PwGroupInterface currentGroup) { - groups.put(rootGroup.getNodeId(), rootGroup); - - super.populateGlobals(currentGroup); - } /** * Ensure that the recycle bin tree exists, if enabled and create it @@ -700,7 +670,7 @@ public class PwDatabaseV4 extends PwDatabase { } PwNodeId recycleId = new PwNodeIdUUID(recycleBinUUID); - return groups.get(recycleId); + return groupIndexes.get(recycleId); } public VariantDictionary getPublicCustomData() { @@ -737,26 +707,6 @@ public class PwDatabaseV4 extends PwDatabase { return true; } - @Override - public void initNew(String dbPath) { - rootGroup = new PwGroupV4(dbNameFromPath(dbPath), iconFactory.getFolderIcon()); - groups.put(rootGroup.getNodeId(), rootGroup); - } - - private String dbNameFromPath(String dbPath) { - String filename = URLUtil.guessFileName(dbPath, null, null); - - if (EmptyUtils.isNullOrEmpty(filename)) { - return "KeePass Database"; - } - int lastExtDot = filename.lastIndexOf("."); - if (lastExtDot == -1) { - return filename; - } - - return filename.substring(0, lastExtDot); - } - @Override public void clearCache() { binPool.clear(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java index 8a73ff91e..065fb8912 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java @@ -164,7 +164,7 @@ public class PwDbHeaderV4 extends PwDbHeader { if (databaseV4.getRootGroup() == null ) { return PwDbHeaderV4.FILE_VERSION_32_3; } - PwGroupInterface.preOrderTraverseTree(databaseV4.getRootGroup(), groupHandler, entryHandler); + PwGroupInterface.doForEachChildAndForRoot(databaseV4.getRootGroup(), entryHandler, groupHandler); if (groupHandler.hasCustomData || entryHandler.hasCustomData) { return PwDbHeaderV4.FILE_VERSION_32_4; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index ba18e1d77..51b007e03 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -137,7 +137,7 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { } }; - protected void updateWith(PwEntryV3 source) { + public void updateWith(PwEntryV3 source) { super.assign(source); title = source.title; username = source.username; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 3f2c14bb8..c027ccd3a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -76,25 +76,6 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte super(parent); } - public void updateWith(PwEntryV4 source) { - super.assign(source); - customIcon = source.customIcon; - usageCount = source.usageCount; - parentGroupLastMod = source.parentGroupLastMod; - customData.clear(); - customData.putAll(source.customData); // Add all custom elements in map - fields = source.fields; - binaries = source.binaries; - foregroundColor = source.foregroundColor; - backgroupColor = source.backgroupColor; - overrideURL = source.overrideURL; - autoType = source.autoType; - history = source.history; - url = source.url; - additional = source.additional; - tags = source.tags; - } - public PwEntryV4(Parcel parcel) { super(parcel); customIcon = parcel.readParcelable(PwIconCustom.class.getClassLoader()); @@ -144,6 +125,25 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte } }; + public void updateWith(PwEntryV4 source) { + super.assign(source); + customIcon = source.customIcon; + usageCount = source.usageCount; + parentGroupLastMod = source.parentGroupLastMod; + customData.clear(); + customData.putAll(source.customData); // Add all custom elements in map + fields = source.fields; + binaries = source.binaries; + foregroundColor = source.foregroundColor; + backgroupColor = source.backgroupColor; + overrideURL = source.overrideURL; + autoType = source.autoType; + history = source.history; + url = source.url; + additional = source.additional; + tags = source.tags; + } + @SuppressWarnings("unchecked") @Override public PwEntryV4 clone() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java index a9df8516b..ffd47b045 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java @@ -9,56 +9,51 @@ import java.util.List; public interface PwGroupInterface extends PwNodeInterface { - List getChildGroups(); + int getLevel(); + + void setLevel(int level); + + List getChildGroups(); List getChildEntries(); - void setGroups(List groups); - - void setEntries(List entries); - - int getLevel(); - void addChildGroup(PwGroupInterface group); void addChildEntry(PwEntryInterface entry); - PwGroupInterface getChildGroupAt(int number); - - PwEntryInterface getChildEntryAt(int number); - void removeChildGroup(PwGroupInterface group); void removeChildEntry(PwEntryInterface entry); - int numbersOfChildGroups(); - - int numbersOfChildEntries(); - boolean containsParent(); /** * Filter MetaStream entries and return children * @return List of direct children (one level below) as PwNode */ - List getDirectChildren(); + List getChildrenWithoutMetastream(); + + boolean allowAddEntryIfIsRoot(); PwGroupInterface duplicate(); - static boolean preOrderTraverseTree(@NonNull PwGroupInterface root, - GroupHandler groupHandler, - EntryHandler entryHandler) { - if (entryHandler != null) { - for (PwEntryInterface entry : root.getChildEntries()) { - if (!entryHandler.operate(entry)) return false; - } - } + static void doForEachChildAndForRoot(@NonNull PwGroupInterface root, + EntryHandler entryHandler, + GroupHandler groupHandler) { + doForEachChild(root, entryHandler, groupHandler); + groupHandler.operate(root); + } + + static boolean doForEachChild(@NonNull PwGroupInterface root, + EntryHandler entryHandler, + GroupHandler groupHandler) { + for (PwEntryInterface entry : root.getChildEntries()) { + if (!entryHandler.operate(entry)) return false; + } for (PwGroupInterface group : root.getChildGroups()) { if ((groupHandler != null) && !groupHandler.operate(group)) return false; - preOrderTraverseTree(group, groupHandler, entryHandler); + doForEachChild(group, entryHandler, groupHandler); } return true; } - - boolean allowAddEntryIfIsRoot(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 1ad12ee43..7f14bda43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -123,10 +123,12 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { this.setNodeId(new PwNodeIdInt(groupId)); } + @Override public int getLevel() { return level; } + @Override public void setLevel(int level) { this.level = level; } @@ -144,17 +146,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { return getTitle(); } - public void populateBlankFields(PwDatabaseV3 db) { - // TODO populate blanck field - if (icon == null) { - icon = db.getIconFactory().getFolderIcon(); - } - - if (title == null) { - title = ""; - } - } - @Override public String getTitle() { return title; @@ -175,16 +166,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { return childEntries; } - @Override - public void setGroups(List groups) { - childGroups = groups; - } - - @Override - public void setEntries(List entries) { - childEntries = entries; - } - @Override public void addChildGroup(PwGroupInterface group) { this.childGroups.add(group); @@ -195,16 +176,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { this.childEntries.add(entry); } - @Override - public PwGroupInterface getChildGroupAt(int number) { - return this.childGroups.get(number); - } - - @Override - public PwEntryInterface getChildEntryAt(int number) { - return this.childEntries.get(number); - } - @Override public void removeChildGroup(PwGroupInterface group) { this.childGroups.remove(group); @@ -216,17 +187,7 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { } @Override - public int numbersOfChildGroups() { - return childGroups.size(); - } - - @Override - public int numbersOfChildEntries() { - return childEntries.size(); - } - - @Override - public List getDirectChildren() { + public List getChildrenWithoutMetastream() { List children = new ArrayList<>(childGroups); for(PwEntryInterface child : childEntries) { if (!child.isMetaStream()) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 76771dad9..8dd1c8c66 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -169,17 +169,6 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter public Type getType() { return Type.GROUP; } - - public void addGroup(PwGroupV4 subGroup) { - if ( subGroup == null ) throw new RuntimeException("subGroup"); - childGroups.add(subGroup); - subGroup.parent = this; - } - - public void addEntry(PwEntryV4 pe) { - addChildEntry(pe); - pe.setParent(this); - } @Override public PwDate getLocationChanged() { @@ -211,11 +200,6 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter expires = exp; } - @Override - public boolean allowAddEntryIfIsRoot() { - return true; - } - @Override public PwIcon getIcon() { if (customIcon == null || customIcon.getUUID().equals(PwDatabase.UUID_ZERO)) { @@ -332,21 +316,16 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter return childEntries; } - @Override - public void setGroups(List groups) { - childGroups = groups; - } - - @Override - public void setEntries(List entries) { - childEntries = entries; - } - @Override public int getLevel() { return -1; // TODO Level } + @Override + public void setLevel(int level) { + // Do nothing here + } + @Override public void addChildGroup(PwGroupInterface group) { this.childGroups.add(group); @@ -357,16 +336,6 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter this.childEntries.add(entry); } - @Override - public PwGroupInterface getChildGroupAt(int number) { - return this.childGroups.get(number); - } - - @Override - public PwEntryInterface getChildEntryAt(int number) { - return this.childEntries.get(number); - } - @Override public void removeChildGroup(PwGroupInterface group) { this.childGroups.remove(group); @@ -378,17 +347,7 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter } @Override - public int numbersOfChildGroups() { - return childGroups.size(); - } - - @Override - public int numbersOfChildEntries() { - return childEntries.size(); - } - - @Override - public List getDirectChildren() { + public List getChildrenWithoutMetastream() { List children = new ArrayList<>(childGroups); for(PwEntryInterface child : childEntries) { if (!child.isMetaStream()) @@ -396,4 +355,9 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter } return children; } + + @Override + public boolean allowAddEntryIfIsRoot() { + return true; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index 6f83f869d..ee6feba33 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -32,13 +32,13 @@ import org.joda.time.LocalDate; */ public abstract class PwNode implements PwNodeInterface, Parcelable, Cloneable { - protected PwNodeId nodeId = initNodeId(); + private PwNodeId nodeId = initNodeId(); protected PwGroupInterface parent = null; protected PwIcon icon = new PwIconStandard(); protected PwDate creation = new PwDate(); - protected PwDate lastMod = new PwDate(); - protected PwDate lastAccess = new PwDate(); - protected PwDate expireDate = PwDate.PW_NEVER_EXPIRE; + private PwDate lastMod = new PwDate(); + private PwDate lastAccess = new PwDate(); + private PwDate expireDate = PwDate.PW_NEVER_EXPIRE; abstract PwNodeId initNodeId(); @@ -54,7 +54,7 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo // TODO better technique ? try { PwNodeId pwGroupId = in.readParcelable(PwNodeId.class.getClassLoader()); - parent = App.getDB().getPwDatabase().getGroupByGroupId(pwGroupId); + parent = App.getDB().getPwDatabase().getGroupById(pwGroupId); } catch (Exception e) { e.printStackTrace(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index 0f3055104..5f4fd6bb9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -54,7 +54,9 @@ import com.kunzisoft.keepass.database.element.PwDate; import com.kunzisoft.keepass.database.element.PwDbHeader; import com.kunzisoft.keepass.database.element.PwDbHeaderV3; import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV3; +import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.database.element.PwNodeIdUUID; import com.kunzisoft.keepass.database.exception.InvalidAlgorithmException; @@ -96,6 +98,8 @@ public class ImporterV3 extends Importer { private static final String TAG = ImporterV3.class.getName(); + private PwDatabaseV3 databaseToOpen; + public ImporterV3() { super(); } @@ -134,8 +138,6 @@ public class ImporterV3 extends Importer { public PwDatabaseV3 openDatabase(InputStream inStream, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater) throws IOException, InvalidDBException { - PwDatabaseV3 databaseToOpen; - // Load entire file, most of it's encrypted. int fileSize = inStream.available(); byte[] filebuf = new byte[fileSize + 16]; // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer @@ -238,8 +240,12 @@ public class ImporterV3 extends Importer { throw new InvalidPasswordException(); } - // Import all groups + // New manual root because V3 contains multiple root groups (here available with getRootGroups()) + PwGroupV3 newRoot = new PwGroupV3(); + newRoot.setLevel(-1); + databaseToOpen.setRootGroup(newRoot); + // Import all groups int pos = PwDbHeaderV3.BUF_SIZE; PwGroupV3 newGrp = new PwGroupV3(); for( int i = 0; i < hdr.numGroups; ) { @@ -249,10 +255,8 @@ public class ImporterV3 extends Importer { pos += 4; if( fieldType == 0xFFFF ) { - // End-Group record. Save group and count it. - newGrp.populateBlankFields(databaseToOpen); - databaseToOpen.addGroup(newGrp); + databaseToOpen.addGroupIndex(newGrp); newGrp = new PwGroupV3(); i++; } @@ -270,7 +274,7 @@ public class ImporterV3 extends Importer { if( fieldType == 0xFFFF ) { // End-Group record. Save group and count it. - databaseToOpen.addEntry(newEnt); + databaseToOpen.addEntryIndex(newEnt); newEnt = new PwEntryV3(); i++; } @@ -280,11 +284,51 @@ public class ImporterV3 extends Importer { pos += 2 + 4 + fieldSize; } - databaseToOpen.constructTree(null); + constructTreeFromIndex(databaseToOpen.getRootGroup()); return databaseToOpen; } + private void constructTreeFromIndex(PwGroupInterface currentGroup) { + + assignGroupsChildren(currentGroup); + assignEntriesChildren(currentGroup); + + // set parent in child entries (normally useless but to be sure or to update parent metadata) + for (PwEntryInterface childEntry : currentGroup.getChildEntries()) { + childEntry.setParent(currentGroup); + } + // recursively construct child groups + for (PwGroupInterface childGroup : currentGroup.getChildGroups()) { + childGroup.setParent(currentGroup); + constructTreeFromIndex(childGroup); + } + } + + private void assignGroupsChildren(PwGroupInterface parent) { + int levelToCheck = parent.getLevel() + 1; + boolean startFromParentPosition = false; + for (PwGroupInterface groupToCheck: databaseToOpen.getGroupIndexes()) { + if (databaseToOpen.getRootGroup().getNodeId().equals(parent.getNodeId()) + || groupToCheck.getNodeId().equals(parent.getNodeId())) { + startFromParentPosition = true; + } + if (startFromParentPosition) { + if (groupToCheck.getLevel() < levelToCheck) + break; + else if (groupToCheck.getLevel() == levelToCheck) + parent.addChildGroup(groupToCheck); + } + } + } + + private void assignEntriesChildren(PwGroupInterface parent) { + for (PwEntryInterface entry : databaseToOpen.getEntryIndexes()) { + if (entry.getParent().getNodeId().equals(parent.getNodeId())) + parent.addChildEntry(entry); + } + } + /** * Parse and save one record from binary file. * @param buf diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 1c17988fe..be0690d67 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -78,7 +78,7 @@ import biz.source_code.base64Coder.Base64Coder; public class ImporterV4 extends Importer { private StreamCipher randomStream; - private PwDatabaseV4 db; + private PwDatabaseV4 mDatabase; private byte[] hashOfHeader = null; private long version; @@ -102,10 +102,10 @@ public class ImporterV4 extends Importer { if (progressTaskUpdater != null) progressTaskUpdater.updateMessage(R.string.retrieving_db_key); - db = new PwDatabaseV4(); + mDatabase = new PwDatabaseV4(); - PwDbHeaderV4 header = new PwDbHeaderV4(db); - db.getBinPool().clear(); + PwDbHeaderV4 header = new PwDbHeaderV4(mDatabase); + mDatabase.getBinPool().clear(); PwDbHeaderV4.HeaderAndHash hh = header.loadFromFile(inStream); version = header.getVersion(); @@ -113,18 +113,18 @@ public class ImporterV4 extends Importer { hashOfHeader = hh.hash; byte[] pbHeader = hh.header; - db.retrieveMasterKey(password, keyInputStream); - db.makeFinalKey(header.masterSeed); + mDatabase.retrieveMasterKey(password, keyInputStream); + mDatabase.makeFinalKey(header.masterSeed); if (progressTaskUpdater != null) progressTaskUpdater.updateMessage(R.string.decrypting_db); CipherEngine engine; Cipher cipher; try { - engine = CipherFactory.getInstance(db.getDataCipher()); - db.setDataEngine(engine); - db.setEncryptionAlgorithm(engine.getPwEncryptionAlgorithm()); - cipher = engine.getCipher(Cipher.DECRYPT_MODE, db.getFinalKey(), header.encryptionIV); + engine = CipherFactory.getInstance(mDatabase.getDataCipher()); + mDatabase.setDataEngine(engine); + mDatabase.setEncryptionAlgorithm(engine.getPwEncryptionAlgorithm()); + cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.getFinalKey(), header.encryptionIV); } catch (NoSuchAlgorithmException|NoSuchPaddingException|InvalidKeyException|InvalidAlgorithmParameterException e) { throw new IOException("Invalid algorithm.", e); } @@ -157,7 +157,7 @@ public class ImporterV4 extends Importer { throw new InvalidDBException(); } - byte[] hmacKey = db.getHmacKey(); + byte[] hmacKey = mDatabase.getHmacKey(); byte[] headerHmac = PwDbHeaderV4.computeHeaderHmac(pbHeader, hmacKey); byte[] storedHmac = isData.readBytes(32); if (storedHmac == null || storedHmac.length != 32) { @@ -174,7 +174,7 @@ public class ImporterV4 extends Importer { } InputStream isXml; - if ( db.getCompressionAlgorithm() == PwCompressionAlgorithm.Gzip ) { + if ( mDatabase.getCompressionAlgorithm() == PwCompressionAlgorithm.Gzip ) { isXml = new GZIPInputStream(isPlain); } else { isXml = isPlain; @@ -196,9 +196,9 @@ public class ImporterV4 extends Importer { ReadXmlStreamed(isXml); - return db; - - + mDatabase.populateNodeIndex(); + + return mDatabase; } private InputStream AttachCipherStream(InputStream is, Cipher cipher) { @@ -214,7 +214,7 @@ public class ImporterV4 extends Importer { } private String getUnusedCacheFileName() { - return String.valueOf(db.getBinPool().findUnusedKey()); + return String.valueOf(mDatabase.getBinPool().findUnusedKey()); } private boolean ReadInnerHeader(LEDataInputStream lis, PwDbHeaderV4 header) throws IOException { @@ -251,7 +251,7 @@ public class ImporterV4 extends Importer { lis.readBytes(byteLength, outputStream::write); } ProtectedBinary protectedBinary = new ProtectedBinary(protectedFlag, file, byteLength); - db.getBinPool().add(protectedBinary); + mDatabase.getBinPool().add(protectedBinary); break; default: @@ -314,7 +314,7 @@ public class ImporterV4 extends Importer { private String entryCustomDataValue = null; private void ReadXmlStreamed(InputStream readerStream) throws IOException, InvalidDBException { - + try { ReadDocumentStreamed(CreatePullParser(readerStream)); } catch (XmlPullParserException e) { @@ -399,54 +399,54 @@ public class ImporterV4 extends Importer { } } } else if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemSettingsChanged)) { - db.setSettingsChanged(ReadPwTime(xpp)); + mDatabase.setSettingsChanged(ReadPwTime(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbName) ) { - db.setName(ReadString(xpp)); + mDatabase.setName(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbNameChanged) ) { - db.setNameChanged(ReadPwTime(xpp)); + mDatabase.setNameChanged(ReadPwTime(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDesc) ) { - db.setDescription(ReadString(xpp)); + mDatabase.setDescription(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDescChanged) ) { - db.setDescriptionChanged(ReadPwTime(xpp)); + mDatabase.setDescriptionChanged(ReadPwTime(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDefaultUser) ) { - db.setDefaultUserName(ReadString(xpp)); + mDatabase.setDefaultUserName(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDefaultUserChanged) ) { - db.setDefaultUserNameChanged(ReadPwTime(xpp)); + mDatabase.setDefaultUserNameChanged(ReadPwTime(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbColor)) { // TODO: Add support to interpret the color if we want to allow changing the database color - db.setColor(ReadString(xpp)); + mDatabase.setColor(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbMntncHistoryDays) ) { - db.setMaintenanceHistoryDays(ReadUInt(xpp, DEFAULT_HISTORY_DAYS)); + mDatabase.setMaintenanceHistoryDays(ReadUInt(xpp, DEFAULT_HISTORY_DAYS)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChanged) ) { - db.setKeyLastChanged(ReadPwTime(xpp)); + mDatabase.setKeyLastChanged(ReadPwTime(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChangeRec) ) { - db.setKeyChangeRecDays(ReadLong(xpp, -1)); + mDatabase.setKeyChangeRecDays(ReadLong(xpp, -1)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChangeForce) ) { - db.setKeyChangeForceDays(ReadLong(xpp, -1)); + mDatabase.setKeyChangeForceDays(ReadLong(xpp, -1)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChangeForceOnce) ) { - db.setKeyChangeForceOnce(ReadBool(xpp, false)); + mDatabase.setKeyChangeForceOnce(ReadBool(xpp, false)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemMemoryProt) ) { return SwitchContext(ctx, KdbContext.MemoryProtection, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIcons) ) { return SwitchContext(ctx, KdbContext.CustomIcons, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemRecycleBinEnabled) ) { - db.setRecycleBinEnabled(ReadBool(xpp, true)); + mDatabase.setRecycleBinEnabled(ReadBool(xpp, true)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemRecycleBinUuid) ) { - db.setRecycleBinUUID(ReadUuid(xpp)); + mDatabase.setRecycleBinUUID(ReadUuid(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemRecycleBinChanged) ) { - db.setRecycleBinChanged(ReadTime(xpp)); + mDatabase.setRecycleBinChanged(ReadTime(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntryTemplatesGroup) ) { - db.setEntryTemplatesGroup(ReadUuid(xpp)); + mDatabase.setEntryTemplatesGroup(ReadUuid(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntryTemplatesGroupChanged) ) { - db.setEntryTemplatesGroupChanged(ReadPwTime(xpp)); + mDatabase.setEntryTemplatesGroupChanged(ReadPwTime(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemHistoryMaxItems) ) { - db.setHistoryMaxItems(ReadInt(xpp, -1)); + mDatabase.setHistoryMaxItems(ReadInt(xpp, -1)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemHistoryMaxSize) ) { - db.setHistoryMaxSize(ReadLong(xpp, -1)); + mDatabase.setHistoryMaxSize(ReadLong(xpp, -1)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLastSelectedGroup) ) { - db.setLastSelectedGroup(ReadUuid(xpp)); + mDatabase.setLastSelectedGroup(ReadUuid(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLastTopVisibleGroup) ) { - db.setLastTopVisibleGroup(ReadUuid(xpp)); + mDatabase.setLastTopVisibleGroup(ReadUuid(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemBinaries) ) { return SwitchContext(ctx, KdbContext.Binaries, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData) ) { @@ -456,17 +456,17 @@ public class ImporterV4 extends Importer { case MemoryProtection: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtTitle) ) { - db.getMemoryProtection().protectTitle = ReadBool(xpp, false); + mDatabase.getMemoryProtection().protectTitle = ReadBool(xpp, false); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtUserName) ) { - db.getMemoryProtection().protectUserName = ReadBool(xpp, false); + mDatabase.getMemoryProtection().protectUserName = ReadBool(xpp, false); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtPassword) ) { - db.getMemoryProtection().protectPassword = ReadBool(xpp, false); + mDatabase.getMemoryProtection().protectPassword = ReadBool(xpp, false); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtURL) ) { - db.getMemoryProtection().protectUrl = ReadBool(xpp, false); + mDatabase.getMemoryProtection().protectUrl = ReadBool(xpp, false); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtNotes) ) { - db.getMemoryProtection().protectNotes = ReadBool(xpp, false); + mDatabase.getMemoryProtection().protectNotes = ReadBool(xpp, false); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtAutoHide) ) { - db.getMemoryProtection().autoEnableVisualHiding = ReadBool(xpp, false); + mDatabase.getMemoryProtection().autoEnableVisualHiding = ReadBool(xpp, false); } else { ReadUnknown(xpp); } @@ -501,7 +501,7 @@ public class ImporterV4 extends Importer { if ( key != null ) { ProtectedBinary pbData = ReadProtectedBinary(xpp); int id = Integer.parseInt(key); - db.getBinPool().put(id, pbData); + mDatabase.getBinPool().put(id, pbData); } else { ReadUnknown(xpp); } @@ -535,7 +535,7 @@ public class ImporterV4 extends Importer { throw new IOException("Group list should be empty."); PwGroupV4 rootGroup = new PwGroupV4(); - db.setRootGroup(rootGroup); + mDatabase.setRootGroup(rootGroup); ctxGroups.push(rootGroup); ctxGroup = ctxGroups.peek(); @@ -555,9 +555,9 @@ public class ImporterV4 extends Importer { } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemNotes) ) { ctxGroup.setNotes(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { - ctxGroup.setIconStandard(db.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); + ctxGroup.setIconStandard(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { - ctxGroup.setIconCustom(db.getIconFactory().getIcon(ReadUuid(xpp))); + ctxGroup.setIconCustom(mDatabase.getIconFactory().getIcon(ReadUuid(xpp))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemTimes) ) { return SwitchContext(ctx, KdbContext.GroupTimes, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIsExpanded) ) { @@ -573,14 +573,17 @@ public class ImporterV4 extends Importer { } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData) ) { return SwitchContext(ctx, KdbContext.GroupCustomData, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - ctxGroup = new PwGroupV4(); - ctxGroups.peek().addGroup(ctxGroup); - ctxGroups.push(ctxGroup); + ctxGroup = new PwGroupV4(); + PwGroupV4 groupPeek = ctxGroups.peek(); + groupPeek.addChildGroup(ctxGroup); + ctxGroup.setParent(groupPeek); + ctxGroups.push(ctxGroup); return SwitchContext(ctx, KdbContext.Group, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { ctxEntry = new PwEntryV4(); - ctxGroup.addEntry(ctxEntry); + ctxGroup.addChildEntry(ctxEntry); + ctxEntry.setParent(ctxGroup); entryInHistory = false; return SwitchContext(ctx, KdbContext.Entry, xpp); @@ -610,9 +613,9 @@ public class ImporterV4 extends Importer { if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { ctxEntry.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { - ctxEntry.setIconStandard(db.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); + ctxEntry.setIconStandard(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { - ctxEntry.setIconCustom(db.getIconFactory().getIcon(ReadUuid(xpp))); + ctxEntry.setIconCustom(mDatabase.getIconFactory().getIcon(ReadUuid(xpp))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemFgColor) ) { ctxEntry.setForegroundColor(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemBgColor) ) { @@ -744,7 +747,7 @@ public class ImporterV4 extends Importer { case RootDeletedObjects: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDeletedObject) ) { ctxDeletedObject = new PwDeletedObject(); - db.addDeletedObject(ctxDeletedObject); + mDatabase.addDeletedObject(ctxDeletedObject); return SwitchContext(ctx, KdbContext.DeletedObject, xpp); } else { @@ -787,8 +790,8 @@ public class ImporterV4 extends Importer { } else if ( ctx == KdbContext.CustomIcon && name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconItem) ) { if ( ! customIconID.equals(PwDatabase.UUID_ZERO) ) { PwIconCustom icon = new PwIconCustom(customIconID, customIconData); - db.addCustomIcon(icon); - db.getIconFactory().put(icon); + mDatabase.addCustomIcon(icon); + mDatabase.getIconFactory().put(icon); } customIconID = PwDatabase.UUID_ZERO; @@ -801,7 +804,7 @@ public class ImporterV4 extends Importer { return KdbContext.Meta; } else if ( ctx == KdbContext.CustomDataItem && name.equalsIgnoreCase(PwDatabaseV4XML.ElemStringDictExItem) ) { if ( customDataKey != null && customDataValue != null) { - db.putCustomData(customDataKey, customDataValue); + mDatabase.putCustomData(customDataKey, customDataValue); } customDataKey = null; @@ -809,7 +812,7 @@ public class ImporterV4 extends Importer { return KdbContext.CustomData; } else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - if ( ctxGroup.getNodeId() == null || ctxGroup.getNodeId().equals(PwDatabase.UUID_ZERO) ) { + if ( ctxGroup.getNodeId() == null || ctxGroup.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { ctxGroup.setNodeId(new PwNodeIdUUID()); } @@ -837,7 +840,7 @@ public class ImporterV4 extends Importer { return KdbContext.GroupCustomData; } else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { - if ( ctxEntry.getNodeId() == null || ctxEntry.getNodeId().equals(PwDatabase.UUID_ZERO) ) { + if ( ctxEntry.getNodeId() == null || ctxEntry.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { ctxEntry.setNodeId(new PwNodeIdUUID()); } @@ -1049,7 +1052,7 @@ public class ImporterV4 extends Importer { xpp.next(); // Consume end tag int id = Integer.parseInt(ref); - return db.getBinPool().get(id); + return mDatabase.getBinPool().get(id); } boolean compressed = false; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java index 51e80288e..4b72e00ac 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java @@ -24,6 +24,7 @@ import com.kunzisoft.keepass.database.element.PwDatabaseV3; import com.kunzisoft.keepass.database.element.PwDbHeader; import com.kunzisoft.keepass.database.element.PwDbHeaderV3; import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; +import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwGroupV3; @@ -50,19 +51,20 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class PwDbV3Output extends PwDbOutput { - private PwDatabaseV3 mPM; + + private PwDatabaseV3 mDatabaseV3; private byte[] headerHashBlock; public PwDbV3Output(PwDatabaseV3 pm, OutputStream os) { super(os); - mPM = pm; + mDatabaseV3 = pm; } public byte[] getFinalKey(PwDbHeader header) throws PwDbOutputException { try { PwDbHeaderV3 h3 = (PwDbHeaderV3) header; - mPM.makeFinalKey(h3.masterSeed, h3.transformSeed, mPM.getNumberKeyEncryptionRounds()); - return mPM.getFinalKey(); + mDatabaseV3.makeFinalKey(h3.masterSeed, h3.transformSeed, mDatabaseV3.getNumberKeyEncryptionRounds()); + return mDatabaseV3.getFinalKey(); } catch (IOException e) { throw new PwDbOutputException("Key creation failed.", e); } @@ -70,7 +72,9 @@ public class PwDbV3Output extends PwDbOutput { @Override public void output() throws PwDbOutputException { - prepForOutput(); + // 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 + sortGroupsForOutput(); PwDbHeader header = outputHeader(mOS); @@ -78,9 +82,9 @@ public class PwDbV3Output extends PwDbOutput { Cipher cipher; try { - if (mPM.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { + if (mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); - } else if (mPM.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish){ + } else if (mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish){ cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING"); } else { throw new Exception(); @@ -105,11 +109,6 @@ public class PwDbV3Output extends PwDbOutput { throw new PwDbOutputException("Failed to output final encrypted part.", e); } } - - private void prepForOutput() { - // 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 - sortGroupsForOutput(); - } @Override protected SecureRandom setIVs(PwDbHeaderV3 header) throws PwDbOutputException { @@ -126,18 +125,18 @@ public class PwDbV3Output extends PwDbOutput { header.signature2 = PwDbHeaderV3.DBSIG_2; header.flags = PwDbHeaderV3.FLAG_SHA2; - if ( mPM.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { + if ( mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { header.flags |= PwDbHeaderV3.FLAG_RIJNDAEL; - } else if ( mPM.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) { + } else if ( mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) { header.flags |= PwDbHeaderV3.FLAG_TWOFISH; } else { throw new PwDbOutputException("Unsupported algorithm."); } header.version = PwDbHeaderV3.DBVER_DW; - header.numGroups = mPM.numberOfGroups(); - header.numEntries = mPM.numberOfEntries(); - header.numKeyEncRounds = (int) mPM.getNumberKeyEncryptionRounds(); + header.numGroups = mDatabaseV3.numberOfGroups(); + header.numEntries = mDatabaseV3.numberOfEntries(); + header.numKeyEncRounds = (int) mDatabaseV3.getNumberKeyEncryptionRounds(); setIVs(header); @@ -216,10 +215,8 @@ public class PwDbV3Output extends PwDbOutput { } // Groups - List groups = mPM.getGroups(); - for ( int i = 0; i < groups.size(); i++ ) { - PwGroupV3 pg = (PwGroupV3) groups.get(i); - PwGroupOutputV3 pgo = new PwGroupOutputV3(pg, os); + for (PwGroupInterface group: mDatabaseV3.getGroupIndexes()) { + PwGroupOutputV3 pgo = new PwGroupOutputV3((PwGroupV3) group, os); try { pgo.output(); } catch (IOException e) { @@ -228,9 +225,8 @@ public class PwDbV3Output extends PwDbOutput { } // Entries - for (int i = 0; i < mPM.numberOfEntries(); i++ ) { - PwEntryV3 pe = (PwEntryV3) mPM.getEntryAt(i); - PwEntryOutputV3 peo = new PwEntryOutputV3(pe, os); + for (PwEntryInterface entry : mDatabaseV3.getEntryIndexes()) { + PwEntryOutputV3 peo = new PwEntryOutputV3((PwEntryV3) (entry), os); try { peo.output(); } catch (IOException e) { @@ -241,14 +237,11 @@ public class PwDbV3Output extends PwDbOutput { private void sortGroupsForOutput() { List groupList = new ArrayList<>(); - // Rebuild list according to coalation sorting order removing any orphaned groups - List roots = mPM.getGrpRoots(); - for ( int i = 0; i < roots.size(); i++ ) { - sortGroup(roots.get(i), groupList); + for (PwGroupInterface rootGroup : mDatabaseV3.getRootGroups()) { + sortGroup(rootGroup, groupList); } - - mPM.setGroups(groupList); + mDatabaseV3.setGroupIndexes(groupList); } private void sortGroup(PwGroupInterface group, List groupList) { @@ -256,8 +249,8 @@ public class PwDbV3Output extends PwDbOutput { groupList.add(group); // Recurse over children - for ( int i = 0; i < group.numbersOfChildGroups(); i++ ) { - sortGroup(group.getChildGroupAt(i), groupList); + for (PwGroupInterface childGroup : group.getChildGroups()) { + sortGroup(childGroup, groupList); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index 25b118722..a59ef3a00 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -161,43 +161,45 @@ public class PwDbV4Output extends PwDbOutput { Stack groupStack = new Stack<>(); groupStack.push(root); - if (!PwGroupInterface.preOrderTraverseTree(root, new GroupHandler() { - @Override - public boolean operate(PwGroupInterface groupInterface) { - PwGroupV4 group = (PwGroupV4) groupInterface; + if (!PwGroupInterface.doForEachChild(root, + new EntryHandler() { + @Override + public boolean operate(PwEntryInterface entryInterface) { + PwEntryV4 entry = (PwEntryV4) entryInterface; - while (true) { - try { - if (group.getParent() == groupStack.peek()) { - groupStack.push(group); - startGroup(group); - break; - } else { - groupStack.pop(); - if (groupStack.size() <= 0) return false; - endGroup(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } + try { + writeEntry(entry, false); + } catch (IOException ex) { + throw new RuntimeException(ex); + } - return true; - } - }, new EntryHandler() { - @Override - public boolean operate(PwEntryInterface entryInterface) { - PwEntryV4 entry = (PwEntryV4) entryInterface; + return true; + } + }, + new GroupHandler() { + @Override + public boolean operate(PwGroupInterface groupInterface) { + PwGroupV4 group = (PwGroupV4) groupInterface; - try { - writeEntry(entry, false); - } catch (IOException ex) { - throw new RuntimeException(ex); - } + while (true) { + try { + if (group.getParent() == groupStack.peek()) { + groupStack.push(group); + startGroup(group); + break; + } else { + groupStack.pop(); + if (groupStack.size() <= 0) return false; + endGroup(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } - return true; - } - })) throw new RuntimeException("Writing groups failed"); + return true; + } + })) throw new RuntimeException("Writing groups failed"); while (groupStack.size() > 1) { xml.endTag(null, PwDatabaseV4XML.ElemGroup); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java index c03497066..972f1bb01 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java @@ -156,7 +156,6 @@ public class PwEntryOutputV3 { } return dataLen; - } private void writeDate(byte[] type, byte[] date) throws IOException { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index a4e5d8d74..61ca893aa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -24,6 +24,8 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.database.EntryHandler; +import com.kunzisoft.keepass.database.GroupHandler; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDatabaseV3; import com.kunzisoft.keepass.database.element.PwDatabaseV4; @@ -31,16 +33,13 @@ import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; -import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; import java.util.Locale; -import java.util.Queue; public class SearchDbHelper { private final Context mCtx; + private int incrementEntry = 0; public SearchDbHelper(Context ctx) { this.mCtx = ctx; @@ -53,43 +52,42 @@ public class SearchDbHelper { public PwGroupInterface search(PwDatabaseVersion pm, String qStr, int max) { - PwGroupInterface group = pm.createGroup(); - group.setTitle("\"" + qStr + "\""); - group.setEntries(new ArrayList<>()); + PwGroupInterface searchGroup = pm.createGroup(); + searchGroup.setTitle("\"" + qStr + "\""); // Search all entries Locale loc = Locale.getDefault(); - qStr = qStr.toLowerCase(loc); + String finalQStr = qStr.toLowerCase(loc); boolean isOmitBackup = omitBackup(); - // TODO Search from the current group - Queue worklist = new LinkedList<>(); - if (pm.getRootGroup() != null) { - worklist.add(pm.getRootGroup()); - } - while (worklist.size() != 0) { - PwGroupInterface top = worklist.remove(); - - if (pm.isGroupSearchable(top, isOmitBackup)) { - for (PwEntryInterface entry : top.getChildEntries()) { - processEntries(entry, group.getChildEntries(), qStr, loc); - if (group.numbersOfChildEntries() >= max) - return group; - } - - for (PwGroupInterface childGroup : top.getChildGroups()) { - if (childGroup != null) { - worklist.add(childGroup); - } - } - } - } + incrementEntry = 0; + PwGroupInterface.doForEachChild(pm.getRootGroup(), + new EntryHandler() { + @Override + public boolean operate(PwEntryInterface entry) { + if (entryContainsString(entry, finalQStr, loc)) { + searchGroup.addChildEntry(entry); + incrementEntry++; + } + // Stop searching when we have max entries + return incrementEntry <= max; + } + }, + new GroupHandler() { + @Override + public boolean operate(PwGroupInterface group) { + if (pm.isGroupSearchable(group, isOmitBackup)) { + return true; + } + return incrementEntry <= max; + } + }); - return group; + return searchGroup; } - private void processEntries(PwEntryInterface entry, List results, String qStr, Locale loc) { + private boolean entryContainsString(PwEntryInterface entry, String qStr, Locale loc) { // Search all strings in the entry Iterator iter = EntrySearchStringIterator.getInstance(entry); while (iter.hasNext()) { @@ -97,11 +95,11 @@ public class SearchDbHelper { if (str != null && str.length() != 0) { String lower = str.toLowerCase(loc); if (lower.contains(qStr)) { - results.add(entry); - break; + return true; } } } + return false; } public static class SearchDbHelperV3 extends SearchDbHelper{ diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java old mode 100755 new mode 100644 diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index 8b4653644..78bef9cc3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -193,7 +193,7 @@ public class SprEngineV4 { List terms = StrUtil.splitSearchTerms(sp.searchString); if (terms.size() <= 1 || sp.regularExpression) { - PwGroupInterface.preOrderTraverseTree(root, null, new EntrySearchHandlerV4(sp, listStorage)); + PwGroupInterface.doForEachChild(root, new EntrySearchHandlerV4(sp, listStorage), null); return; } @@ -214,7 +214,7 @@ public class SprEngineV4 { negate = sp.searchString.length() > 0; } - if (!PwGroupInterface.preOrderTraverseTree(root, null, new EntrySearchHandlerV4(sp, pgNew))) { + if (!PwGroupInterface.doForEachChild(root, new EntrySearchHandlerV4(sp, pgNew), null)) { pg = null; break; } diff --git a/app/src/main/res/layout/list_nodes_with_add_button.xml b/app/src/main/res/layout/list_nodes_with_add_button.xml index 0cba7aefc..23da8fd97 100644 --- a/app/src/main/res/layout/list_nodes_with_add_button.xml +++ b/app/src/main/res/layout/list_nodes_with_add_button.xml @@ -100,7 +100,7 @@ diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 diff --git a/gradlew.bat b/gradlew.bat old mode 100644 new mode 100755 From dcb819b087a26845bba8d38cf20170d955fd3bdb Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 12 Apr 2019 19:19:13 +0200 Subject: [PATCH 093/289] Fix display message Toast bug --- .../kunzisoft/keepass/tasks/ActionRunnable.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt index 55ee20094..08fcffb68 100644 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt @@ -19,7 +19,9 @@ */ package com.kunzisoft.keepass.tasks +import android.app.Activity import android.content.Context +import android.util.Log import android.widget.Toast /** @@ -71,14 +73,19 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable? abstract fun onFinishRun(isSuccess: Boolean, message: String?) /** - * ONLY to use in UIThread, typically in an Activity, Fragment or a Service - * @param ctx Context to show the message + * Display a message as a Toast only if [context] is an Activity + * @param context Context to show the message */ - protected fun displayMessage(ctx: Context) { - message?.let { - if (it.isNotEmpty()) { - Toast.makeText(ctx, message, Toast.LENGTH_LONG).show() + protected fun displayMessage(context: Context) { + Log.i(ActionRunnable::class.java.name, message) + try { + (context as Activity).runOnUiThread { + message?.let { + if (it.isNotEmpty()) { + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + } } - } + } catch (exception: ClassCastException) {} } } From eba1dbc8fafa019fbca09fb98702482c0eaaaa57 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 12 Apr 2019 21:46:40 +0200 Subject: [PATCH 094/289] Move touch, fix tree construction, refactor getPwDatabase --- .../keepass/activities/EntryActivity.java | 31 +++++---- .../keepass/activities/EntryEditActivity.java | 32 +++++---- .../keepass/activities/GroupActivity.java | 8 ++- .../keepass/adapters/NodeAdapter.java | 7 +- .../database/action/node/AddEntryRunnable.kt | 8 ++- .../database/action/node/AddGroupRunnable.kt | 7 +- .../database/action/node/CopyEntryRunnable.kt | 1 + .../action/node/DeleteEntryRunnable.kt | 1 + .../action/node/DeleteGroupRunnable.java | 1 + .../action/node/UpdateEntryRunnable.kt | 2 +- .../action/node/UpdateGroupRunnable.kt | 2 +- .../keepass/database/element/Database.java | 68 ++++++++++++++----- .../keepass/database/element/PwDatabase.java | 65 +++++++----------- .../database/element/PwDatabaseV4.java | 26 +++---- .../database/element/PwEntryInterface.java | 10 +-- .../keepass/database/element/PwEntryV3.java | 13 ---- .../keepass/database/element/PwEntryV4.java | 16 ++--- .../keepass/database/element/PwGroupV3.java | 4 -- .../keepass/database/element/PwGroupV4.java | 25 +++---- .../keepass/database/element/PwNode.java | 5 -- 20 files changed, 168 insertions(+), 164 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 75150c9f6..722fe4248 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -45,7 +45,6 @@ import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.database.ExtraFields; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.security.ProtectedString; @@ -126,6 +125,9 @@ public class EntryActivity extends LockingHideActivity { return; } + // Update last access time. + mEntry.touch(false, false); + // Retrieve the textColor to tint the icon int[] attrs = {R.attr.textColorInverse}; TypedArray ta = getTheme().obtainStyledAttributes(attrs); @@ -133,9 +135,6 @@ public class EntryActivity extends LockingHideActivity { // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set invalidateOptionsMenu(); - - // Update last access time. - mEntry.touch(false, false); // Get views titleIconView = findViewById(R.id.entry_icon); @@ -156,8 +155,10 @@ public class EntryActivity extends LockingHideActivity { fillData(); invalidateOptionsMenu(); + Database database = App.getDB(); // Start to manage field reference to copy a value from ref - mEntry.startToManageFieldReferences(App.getDB().getPwDatabase()); + if (database != null) + database.startManageEntry(mEntry); boolean containsUsernameToCopy = mEntry.getUsername().length() > 0; @@ -232,7 +233,9 @@ public class EntryActivity extends LockingHideActivity { startService(intent); } - mEntry.stopToManageFieldReferences(); + + if (database != null) + database.stopManageEntry(mEntry); } firstLaunchOfActivity = false; } @@ -311,13 +314,12 @@ public class EntryActivity extends LockingHideActivity { } protected void fillData() { - Database db = App.getDB(); - PwDatabase pm = db.getPwDatabase(); - - mEntry.startToManageFieldReferences(pm); - - // Assign title icon - db.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); + Database database = App.getDB(); + if (database != null) { + database.startManageEntry(mEntry); + // Assign title icon + database.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); + } // Assign title text titleView.setText(PwEntryInterface.getVisualTitle(mEntry)); @@ -395,7 +397,8 @@ public class EntryActivity extends LockingHideActivity { entryContentsView.assignExpiresDate(getString(R.string.never)); } - mEntry.stopToManageFieldReferences(); + if (database != null) + database.stopManageEntry(mEntry); } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 56d280eca..2eee05260 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -47,13 +47,12 @@ import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDate; import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.PwIcon; import com.kunzisoft.keepass.database.element.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; @@ -87,6 +86,7 @@ public class EntryEditActivity extends LockingHideActivity private Database database; protected PwEntryInterface mEntry; + protected PwGroupInterface mParent; protected PwEntryInterface mCallbackNewEntry; protected boolean mIsNew; protected PwIconStandard mSelectedIconStandard; @@ -179,18 +179,17 @@ public class EntryEditActivity extends LockingHideActivity TypedArray ta = getTheme().obtainStyledAttributes(attrs); iconColor = ta.getColor(0, Color.WHITE); - mSelectedIconStandard = database.getPwDatabase().getIconFactory().getUnknownIcon(); + mSelectedIconStandard = database.getIconFactory().getUnknownIcon(); - PwDatabase pm = database.getPwDatabase(); if (keyEntry == null) { PwNodeId parentId = intent.getParcelableExtra(KEY_PARENT); - PwGroupInterface parent = pm.getGroupById(parentId); - mEntry = database.createEntry(parent); + mParent = database.getGroupById(parentId); + mEntry = database.createEntry(); mIsNew = true; // Add the default icon database.getDrawFactory().assignDefaultDatabaseIconTo(this, entryIconView, iconColor); } else { - mEntry = pm.getEntryById(keyEntry); + mEntry = database.getEntryById(keyEntry); mIsNew = false; fillData(); } @@ -277,6 +276,7 @@ public class EntryEditActivity extends LockingHideActivity task = new AddEntryRunnable(EntryEditActivity.this, database, mCallbackNewEntry, + mParent, afterActionNodeFinishRunnable, !getReadOnly()); } else { @@ -416,13 +416,14 @@ public class EntryEditActivity extends LockingHideActivity } protected PwEntryInterface populateNewEntry() { - PwDatabase db = App.getDB().getPwDatabase(); + Database database = App.getDB(); PwEntryInterface newEntry = mEntry.duplicate(); - newEntry.startToManageFieldReferences(db); - - newEntry.createBackup(db); + if (database != null) { + database.startManageEntry(newEntry); + database.createBackupOf(newEntry); + } newEntry.setLastAccessTime(new PwDate()); newEntry.setLastModificationTime(new PwDate()); @@ -448,7 +449,8 @@ public class EntryEditActivity extends LockingHideActivity } } - newEntry.stopToManageFieldReferences(); + if (database != null) + database.stopManageEntry(newEntry); return newEntry; } @@ -462,7 +464,7 @@ public class EntryEditActivity extends LockingHideActivity return mSelectedIconStandard; else { if (mIsNew) { - return database.getPwDatabase().getIconFactory().getKeyIcon(); + return database.getIconFactory().getKeyIcon(); } else { // Keep previous icon, if no new one was selected @@ -512,7 +514,7 @@ public class EntryEditActivity extends LockingHideActivity assignIconView(); // Don't start the field reference manager, we want to see the raw ref - mEntry.stopToManageFieldReferences(); + App.getDB().stopManageEntry(mEntry); entryTitleView.setText(mEntry.getTitle()); entryUserNameView.setText(mEntry.getUsername()); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 41e55b309..847dc4e19 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -255,6 +255,9 @@ public class GroupActivity extends LockingActivity return; } + // Update last access time. + mCurrentGroup.touch(false, false); + toolbar.setTitle(""); setSupportActionBar(toolbar); @@ -976,14 +979,17 @@ public class GroupActivity extends LockingActivity case CREATION: // If group creation // Build the group - PwGroupInterface newGroup = database.createGroup(mCurrentGroup); + PwGroupInterface newGroup = database.createGroup(); newGroup.setTitle(name); newGroup.setIcon(icon); + // Not really needed here because added in runnable but safe + newGroup.setParent(mCurrentGroup); // If group created save it in the database new Thread(new AddGroupRunnable(this, App.getDB(), newGroup, + mCurrentGroup, new AfterAddNodeRunnable(), !getReadOnly()) ).start(); diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 4fbb1179e..7eb164d61 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -250,7 +250,9 @@ public class NodeAdapter extends RecyclerView.Adapter { holder.subText.setVisibility(View.GONE); if (subNode.getType().equals(PwNodeInterface.Type.ENTRY)) { PwEntryInterface entry = (PwEntryInterface) subNode; - entry.startToManageFieldReferences(database.getPwDatabase()); + + if (database != null) + database.startManageEntry(entry); holder.text.setText(PwEntryInterface.getVisualTitle(entry)); @@ -260,7 +262,8 @@ public class NodeAdapter extends RecyclerView.Adapter { holder.subText.setText(username); } - entry.stopToManageFieldReferences(); + if (database != null) + database.stopManageEntry(entry); } // Assign image and text size diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt index edbca87f2..e770e13d6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -22,17 +22,21 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.PwEntryInterface +import com.kunzisoft.keepass.database.element.PwGroupInterface class AddEntryRunnable constructor( context: FragmentActivity, database: Database, private val mNewEntry: PwEntryInterface, + private val mParent: PwGroupInterface, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { override fun nodeAction() { - database.addEntryTo(mNewEntry, mNewEntry.parent) + mNewEntry.touch(true, true) + mParent.touch(true, true) + database.addEntryTo(mNewEntry, mParent) } override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt index 79c2ddfc5..5daf7eb05 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt @@ -27,17 +27,20 @@ class AddGroupRunnable constructor( context: FragmentActivity, database: Database, private val mNewGroup: PwGroupInterface, + private val mParent: PwGroupInterface, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { override fun nodeAction() { - database.addGroupTo(mNewGroup, mNewGroup.parent) + mNewGroup.touch(true, true) + mParent.touch(true, true) + database.addGroupTo(mNewGroup, mParent) } override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { if (!isSuccess) { - database.removeGroupFrom(mNewGroup, mNewGroup.parent) + database.removeGroupFrom(mNewGroup, mParent) } return ActionNodeValues(isSuccess, message, null, mNewGroup) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt index 680424625..c04c79a8a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt @@ -38,6 +38,7 @@ class CopyEntryRunnable constructor( override fun nodeAction() { // Update entry with new values + mNewParent.touch(false, true) mEntryCopied = database.copyEntry(mEntryToCopy, mNewParent) mEntryCopied?.apply { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt index 8bb6fbb18..0a8b4c2b4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt @@ -38,6 +38,7 @@ class DeleteEntryRunnable constructor( override fun nodeAction() { mParent = mEntryToDelete.parent + mParent?.touch(false, true) // Remove Entry from parent mRecycle = database.canRecycle(mEntryToDelete) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java index ab94178f6..251dc5245 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java @@ -46,6 +46,7 @@ public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { @Override public void nodeAction() { mParent = mGroupToDelete.getParent(); + mParent.touch(false, true); // Remove Group from parent mRecycle = getDatabase().canRecycle(mGroupToDelete); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt index 74e81ddf5..399e185d8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt @@ -37,8 +37,8 @@ class UpdateEntryRunnable constructor( override fun nodeAction() { // Update entry with new values - database.updateEntry(mOldEntry, mNewEntry) mOldEntry.touch(true, true) + database.updateEntry(mOldEntry, mNewEntry) } override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt index eb4e9e326..4d97a6846 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt @@ -37,8 +37,8 @@ class UpdateGroupRunnable constructor( override fun nodeAction() { // Update group with new values - database.updateGroup(mOldGroup, mNewGroup) mOldGroup.touch(true, true) + database.updateGroup(mOldGroup, mNewGroup) } override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 558ec298a..6421b419b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -74,9 +74,7 @@ public class Database { public boolean loaded = false; - public Database() { - - } + public Database() {} public Database(String databasePath) { // TODO Test with kdb extension @@ -135,6 +133,10 @@ public class Database { return drawFactory; } + public PwIconFactory getIconFactory() { + return pwDatabase.getIconFactory(); + } + public void loadData(Context ctx, Uri uri, String password, Uri keyfile, ProgressTaskUpdater status) throws IOException, FileNotFoundException, InvalidDBException { loadData(ctx, uri, password, keyfile, status, !Importer.DEBUG); } @@ -261,7 +263,7 @@ public class Database { public PwEntryInterface getEntryFrom(Cursor cursor) { PwIconFactory iconFactory = getPwDatabase().getIconFactory(); - PwEntryInterface pwEntry = createEntry(null); + PwEntryInterface pwEntry = createEntry(); try { switch (getPwDatabase().getVersion()) { case V3: @@ -269,9 +271,9 @@ public class Database { break; case V4: // TODO invert field reference manager - pwEntry.startToManageFieldReferences(getPwDatabase()); + startManageEntry(pwEntry); ((EntryCursorV4) cursor).populateEntry((PwEntryV4) pwEntry, iconFactory); - pwEntry.stopToManageFieldReferences(); + stopManageEntry(pwEntry); break; } } catch (Exception e) { @@ -554,13 +556,13 @@ public class Database { } } - public PwEntryInterface createEntry(@Nullable PwGroupInterface parent) { + public PwEntryInterface createEntry() { try { switch (getPwDatabase().getVersion()) { case V3: - return new PwEntryV3((PwGroupV3) parent); + return new PwEntryV3(); case V4: - return new PwEntryV4((PwGroupV4) parent); + return new PwEntryV4(); } } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be created", e); @@ -568,14 +570,14 @@ public class Database { return null; } - public PwGroupInterface createGroup(PwGroupInterface parent) { + public PwGroupInterface createGroup() { PwGroupInterface newPwGroup = null; try { switch (getPwDatabase().getVersion()) { case V3: - newPwGroup = new PwGroupV3((PwGroupV3) parent); + newPwGroup = new PwGroupV3(); case V4: - newPwGroup = new PwGroupV4((PwGroupV4) parent); + newPwGroup = new PwGroupV4(); } newPwGroup.setNodeId(pwDatabase.newGroupId()); } catch (Exception e) { @@ -584,6 +586,14 @@ public class Database { return newPwGroup; } + public PwEntryInterface getEntryById(PwNodeId id) { + return pwDatabase.entryIndexes.get(id); + } + + public PwGroupInterface getGroupById(PwNodeId id) { + return pwDatabase.groupIndexes.get(id); + } + public void addEntryTo(PwEntryInterface entry, PwGroupInterface parent) { try { getPwDatabase().addEntryTo(entry, parent); @@ -718,7 +728,8 @@ public class Database { public void deleteEntry(PwEntryInterface entry) { try { - getPwDatabase().deleteEntry(entry); + PwGroupInterface parent = entry.getParent(); + removeEntryFrom(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be deleted", e); } @@ -730,14 +741,15 @@ public class Database { new EntryHandler() { @Override public boolean operate(PwEntryInterface entry) { - getPwDatabase().deleteEntry(entry); + deleteEntry(entry); return true; } }, new GroupHandler() { @Override public boolean operate(PwGroupInterface group) { - getPwDatabase().deleteGroup(group); + PwGroupInterface parent = group.getParent(); + removeGroupFrom(group, parent); return true; } }); @@ -772,7 +784,7 @@ public class Database { public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface parent) { try { - getPwDatabase().undoDeleteEntry(entry, parent); + getPwDatabase().undoDeleteEntryFrom(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo the deletion of this version of PwEntry", e); } @@ -785,4 +797,28 @@ public class Database { Log.e(TAG, "This version of database can't undo the deletion of this version of PwGroup", e); } } + + public void startManageEntry(PwEntryInterface entry) { + switch (getPwDatabase().getVersion()) { + case V4: + ((PwEntryV4) entry).startToManageFieldReferences((PwDatabaseV4) getPwDatabase()); + break; + } + } + + public void stopManageEntry(PwEntryInterface entry) { + switch (getPwDatabase().getVersion()) { + case V4: + ((PwEntryV4) entry).stopToManageFieldReferences(); + break; + } + } + + public void createBackupOf(PwEntryInterface entry) { + switch (getPwDatabase().getVersion()) { + case V4: + ((PwEntryV4) entry).createBackup((PwDatabaseV4) getPwDatabase()); + break; + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index 1cd37daa0..f137815c7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database.element; +import android.support.annotation.Nullable; + import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.KeyFileEmptyException; import com.kunzisoft.keepass.utils.MemUtil; @@ -275,6 +277,10 @@ public abstract class PwDatabase { this.groupIndexes.put(group.getNodeId(), group); } + public void removeGroupIndex(PwGroupInterface group) { + this.groupIndexes.remove(group.getNodeId()); + } + public int numberOfGroups() { return groupIndexes.size(); } @@ -291,6 +297,10 @@ public abstract class PwDatabase { this.entryIndexes.put(entry.getNodeId(), entry); } + public void removeEntryIndex(PwEntryInterface entry) { + this.entryIndexes.remove(entry.getNodeId()); + } + public int numberOfEntries() { return entryIndexes.size(); } @@ -301,69 +311,44 @@ public abstract class PwDatabase { * ------------------------------------- */ - protected void addGroupTo(PwGroupInterface newGroup, PwGroupInterface parent) { - // Add tree to parent tree - if ( parent == null ) { - parent = rootGroup; - } - - parent.addChildGroup(newGroup); + protected void addGroupTo(PwGroupInterface newGroup, @Nullable PwGroupInterface parent) { + // Add tree to parent tree + if (parent != null) + parent.addChildGroup(newGroup); newGroup.setParent(parent); addGroupIndex(newGroup); - - parent.touch(true, true); } - protected void removeGroupFrom(PwGroupInterface remove, PwGroupInterface parent) { + protected void removeGroupFrom(PwGroupInterface groupToRemove, PwGroupInterface parent) { // Remove tree from parent tree if (parent != null) { - parent.removeChildGroup(remove); + parent.removeChildGroup(groupToRemove); } - groupIndexes.remove(remove.getNodeId()); + removeGroupIndex(groupToRemove); } - protected void addEntryTo(PwEntryInterface newEntry, PwGroupInterface parent) { + protected void addEntryTo(PwEntryInterface newEntry, @Nullable PwGroupInterface parent) { // Add entry to parent - if (parent != null) { - parent.addChildEntry(newEntry); - } + if (parent != null) + parent.addChildEntry(newEntry); newEntry.setParent(parent); - - entryIndexes.put(newEntry.getNodeId(), newEntry); + addEntryIndex(newEntry); } - protected void removeEntryFrom(PwEntryInterface remove, PwGroupInterface parent) { + protected void removeEntryFrom(PwEntryInterface entryToRemove, PwGroupInterface parent) { // Remove entry for parent if (parent != null) { - parent.removeChildEntry(remove); + parent.removeChildEntry(entryToRemove); } - entryIndexes.remove(remove.getNodeId()); + removeEntryIndex(entryToRemove); } - protected void deleteGroup(PwGroupInterface group) { - PwGroupInterface parent = group.getParent(); - removeGroupFrom(group, parent); - parent.touch(false, true); - } - - protected void deleteEntry(PwEntryInterface entry) { - PwGroupInterface parent = entry.getParent(); - removeEntryFrom(entry, parent); - parent.touch(false, true); - } - /* - public void removeGroup(PwGroupV3 tree) { - tree.parent.childGroups.remove(tree); - groups.remove(tree); - } - */ - // TODO Delete group public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface origParent) { addGroupTo(group, origParent); } - public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface origParent) { + public void undoDeleteEntryFrom(PwEntryInterface entry, PwGroupInterface origParent) { addEntryTo(entry, origParent); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 67148ef19..066d952c2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -116,15 +116,15 @@ public class PwDatabaseV4 extends PwDatabase { new EntryHandler() { @Override public boolean operate(PwEntryInterface entry) { - entryIndexes.put(entry.getNodeId(), entry); - return false; + addEntryIndex(entry); + return true; } }, new GroupHandler() { @Override public boolean operate(PwGroupInterface group) { - groupIndexes.put(group.getNodeId(), group); - return false; + addGroupIndex(group); + return true; } }); } @@ -601,12 +601,10 @@ public class PwDatabaseV4 extends PwDatabase { PwGroupInterface parent = group.getParent(); removeGroupFrom(group, parent); - parent.touch(false, true); PwGroupInterface recycleBin = getRecycleBin(); addGroupTo(group, recycleBin); - group.touch(false, true); // TODO ? group.touchLocation(); } @@ -616,12 +614,10 @@ public class PwDatabaseV4 extends PwDatabase { PwGroupInterface parent = entry.getParent(); removeEntryFrom(entry, parent); - parent.touch(false, true); PwGroupInterface recycleBin = getRecycleBin(); addEntryTo(entry, recycleBin); - - entry.touch(false, true); + entry.touchLocation(); } @@ -651,15 +647,15 @@ public class PwDatabaseV4 extends PwDatabase { this.deletedObjects.add(deletedObject); } - @Override - public void deleteEntry(PwEntryInterface entry) { - super.deleteEntry(entry); - deletedObjects.add(new PwDeletedObject((UUID) entry.getNodeId().getId())); + @Override + protected void removeEntryFrom(PwEntryInterface entryToRemove, PwGroupInterface parent) { + super.removeEntryFrom(entryToRemove, parent); + deletedObjects.add(new PwDeletedObject((UUID) entryToRemove.getNodeId().getId())); } @Override - public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface origParent) { - super.undoDeleteEntry(entry, origParent); + public void undoDeleteEntryFrom(PwEntryInterface entry, PwGroupInterface origParent) { + super.undoDeleteEntryFrom(entry, origParent); deletedObjects.remove(new PwDeletedObject((UUID) entry.getNodeId().getId())); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java index 3857ce6c0..f0896c663 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java @@ -69,16 +69,8 @@ public interface PwEntryInterface extends PwNodeInterface, SmallTimeInterface { */ boolean isMetaStream(); - /** - * Create a backup of entry - */ - void createBackup(PwDatabase db); - void touchLocation(); - void startToManageFieldReferences(PwDatabase db); - void stopToManageFieldReferences(); - PwEntryInterface duplicate(); String PMS_TAN_ENTRY = ""; @@ -91,7 +83,7 @@ public interface PwEntryInterface extends PwNodeInterface, SmallTimeInterface { /** * {@inheritDoc} * Get the display title from an entry,
- * {@link #startToManageFieldReferences(PwDatabase)} and {@link #stopToManageFieldReferences()} must be called + * {@link #startManageEntry()} and {@link #stopManageEntry()} must be called * before and after {@link #getVisualTitle(PwEntryInterface entry)} */ static String getVisualTitle(boolean isTan, String title, String userName, String url, String id) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index 51b007e03..d09ef347b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -97,10 +97,6 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { public PwEntryV3() { super(); } - - public PwEntryV3(PwGroupV3 parent) { - super(parent); - } public PwEntryV3(Parcel parcel) { super(parcel); @@ -318,9 +314,6 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { return icon.isMetaStreamIcon(); } - @Override - public void createBackup(PwDatabase db) {} - @Override public boolean isSearchingEnabled() { return false; @@ -331,12 +324,6 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { return false; } - @Override - public void startToManageFieldReferences(PwDatabase db) {} - - @Override - public void stopToManageFieldReferences() {} - @Override public void touchLocation() {} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index c027ccd3a..89217fde9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -71,10 +71,6 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte public PwEntryV4() { super(); } - - public PwEntryV4(PwGroupV4 parent) { - super(parent); - } public PwEntryV4(Parcel parcel) { super(parcel); @@ -181,13 +177,11 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte return Type.ENTRY; } - @Override - public void startToManageFieldReferences(PwDatabase db) { - this.mDatabase = (PwDatabaseV4) db; + public void startToManageFieldReferences(PwDatabaseV4 db) { + this.mDatabase = db; this.mDecodeRef = true; } - @Override public void stopToManageFieldReferences() { this.mDatabase = null; this.mDecodeRef = false; @@ -463,15 +457,13 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte return size; } - @Override - public void createBackup(PwDatabase db) { + public void createBackup(PwDatabaseV4 db) { PwEntryV4 copy = clone(); copy.history = new ArrayList<>(); history.add(copy); if (db != null) - if (db instanceof PwDatabaseV4) - maintainBackups((PwDatabaseV4) db); + maintainBackups(db); } private boolean maintainBackups(PwDatabaseV4 db) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 7f14bda43..1bbf4f1c3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -46,10 +46,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { super(); } - public PwGroupV3(PwGroupV3 parent) { - super(parent); - } - public PwGroupV3(Parcel in) { super(in); title = in.readString(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 8dd1c8c66..f93729ac5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -40,7 +40,7 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter private String title = ""; private PwIconCustom customIcon = PwIconCustom.ZERO; private long usageCount = 0; - private PwDate parentGroupLastMod = new PwDate(); + private PwDate locationChangeDate = new PwDate(); private Map customData = new HashMap<>(); private boolean expires = false; private String notes = ""; @@ -58,11 +58,6 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter public PwGroupV4() { super(); } - - public PwGroupV4(PwGroupV4 parent) { - super(parent); - parentGroupLastMod = new PwDate(); - } public PwGroupV4(String title, PwIconStandard icon) { super(); @@ -75,7 +70,7 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter title = in.readString(); customIcon = in.readParcelable(PwIconCustom.class.getClassLoader()); usageCount = in.readLong(); - parentGroupLastMod = in.readParcelable(PwDate.class.getClassLoader()); + locationChangeDate = in.readParcelable(PwDate.class.getClassLoader()); // TODO customData = MemUtil.readStringParcelableMap(in); expires = in.readByte() != 0; notes = in.readString(); @@ -94,7 +89,7 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter dest.writeString(title); dest.writeParcelable(customIcon, flags); dest.writeLong(usageCount); - dest.writeParcelable(parentGroupLastMod, flags); + dest.writeParcelable(locationChangeDate, flags); // TODO MemUtil.writeStringParcelableMap(dest, customData); dest.writeByte((byte) (expires ? 1 : 0)); dest.writeString(notes); @@ -122,7 +117,7 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter title = source.title; customIcon = source.customIcon; usageCount = source.usageCount; - parentGroupLastMod = source.parentGroupLastMod; + locationChangeDate = source.locationChangeDate; customData = source.customData; expires = source.expires; @@ -145,7 +140,7 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter // name is clone automatically (IMMUTABLE) newGroup.customIcon = new PwIconCustom(this.customIcon); // newGroup.usageCount stay the same in copy - newGroup.parentGroupLastMod = this.parentGroupLastMod.clone(); + newGroup.locationChangeDate = this.locationChangeDate.clone(); // TODO customData make copy from hashmap newGroup.customData = (HashMap) this.customData.clone(); // newGroup.expires stay the same in copy @@ -170,14 +165,20 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter return Type.GROUP; } + @Override + public void setParent(PwGroupInterface parent) { + super.setParent(parent); + locationChangeDate = new PwDate(); + } + @Override public PwDate getLocationChanged() { - return parentGroupLastMod; + return locationChangeDate; } @Override public void setLocationChanged(PwDate date) { - parentGroupLastMod = date; + locationChangeDate = date; } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index ee6feba33..4a0d75621 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -44,11 +44,6 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo protected PwNode() {} - protected PwNode(PwGroupInterface parent) { - this(); - this.parent = parent; - } - protected PwNode(Parcel in) { nodeId = in.readParcelable(PwNodeId.class.getClassLoader()); // TODO better technique ? From d1f88112ce93fe610498a17a54f31e4577e5d0b0 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 12 Apr 2019 22:22:17 +0200 Subject: [PATCH 095/289] Finish database encapsulation --- .../keepass/tests/database/TestData.java | 7 +- .../keepass/activities/EntryActivity.java | 19 +- .../keepass/activities/EntryEditActivity.java | 9 +- .../keepass/activities/GroupActivity.java | 16 +- .../keepass/activities/ListNodesFragment.java | 3 - .../keepass/adapters/NodeAdapter.java | 16 +- .../adapters/SearchEntryCursorAdapter.java | 2 +- .../AssignPasswordInDatabaseRunnable.kt | 19 +- .../keepass/database/element/Database.java | 190 ++++++++++-------- .../keepass/database/element/PwDatabase.java | 7 +- .../database/element/PwDatabaseV4.java | 1 - .../keepass/database/element/PwNode.java | 2 +- .../dialogs/GroupEditDialogFragment.java | 2 +- 13 files changed, 154 insertions(+), 139 deletions(-) diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java index 0e6fa1e27..7f4d059e4 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java @@ -19,8 +19,6 @@ */ package com.kunzisoft.keepass.tests.database; -import java.io.InputStream; - import android.content.Context; import android.content.res.AssetManager; import android.net.Uri; @@ -30,6 +28,8 @@ import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; import com.kunzisoft.keepass.database.load.Importer; import com.kunzisoft.keepass.tests.TestUtil; +import java.io.InputStream; + public class TestData { private static final String TEST1_KEYFILE = ""; private static final String TEST1_KDB = "test1.kdb"; @@ -72,6 +72,7 @@ public class TestData { GetDb1(ctx); } - return (PwDatabaseV3Debug) mDb1.getPwDatabase(); + //return (PwDatabaseV3Debug) mDb1.getPwDatabase(); + return null; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 722fe4248..1dc39b442 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -115,7 +115,7 @@ public class EntryActivity extends LockingHideActivity { PwNodeId keyEntry; try { keyEntry = i.getParcelableExtra(KEY_ENTRY); - mEntry = db.getPwDatabase().getEntryById(keyEntry); + mEntry = db.getEntryById(keyEntry); } catch (ClassCastException e) { Log.e(TAG, "Unable to retrieve the entry key"); } @@ -157,8 +157,7 @@ public class EntryActivity extends LockingHideActivity { Database database = App.getDB(); // Start to manage field reference to copy a value from ref - if (database != null) - database.startManageEntry(mEntry); + database.startManageEntry(mEntry); boolean containsUsernameToCopy = mEntry.getUsername().length() > 0; @@ -234,8 +233,7 @@ public class EntryActivity extends LockingHideActivity { startService(intent); } - if (database != null) - database.stopManageEntry(mEntry); + database.stopManageEntry(mEntry); } firstLaunchOfActivity = false; } @@ -315,11 +313,9 @@ public class EntryActivity extends LockingHideActivity { protected void fillData() { Database database = App.getDB(); - if (database != null) { - database.startManageEntry(mEntry); - // Assign title icon - database.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); - } + database.startManageEntry(mEntry); + // Assign title icon + database.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); // Assign title text titleView.setText(PwEntryInterface.getVisualTitle(mEntry)); @@ -397,8 +393,7 @@ public class EntryActivity extends LockingHideActivity { entryContentsView.assignExpiresDate(getString(R.string.never)); } - if (database != null) - database.stopManageEntry(mEntry); + database.stopManageEntry(mEntry); } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 2eee05260..47303e9f6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -420,10 +420,8 @@ public class EntryEditActivity extends LockingHideActivity PwEntryInterface newEntry = mEntry.duplicate(); - if (database != null) { - database.startManageEntry(newEntry); - database.createBackupOf(newEntry); - } + database.startManageEntry(newEntry); + database.createBackupOf(newEntry); newEntry.setLastAccessTime(new PwDate()); newEntry.setLastModificationTime(new PwDate()); @@ -449,8 +447,7 @@ public class EntryEditActivity extends LockingHideActivity } } - if (database != null) - database.stopManageEntry(newEntry); + database.stopManageEntry(newEntry); return newEntry; } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 847dc4e19..8249b4d5f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -72,11 +72,10 @@ import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable; import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable; import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwIcon; +import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.PwNodeInterface; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; @@ -242,7 +241,7 @@ public class GroupActivity extends LockingActivity } try { - rootGroup = database.getPwDatabase().getRootGroup(); + rootGroup = database.getRootGroup(); } catch (NullPointerException e) { Log.e(TAG, "Unable to get rootGroup"); } @@ -400,7 +399,7 @@ public class GroupActivity extends LockingActivity if (pwGroupId == null) { currentGroup = rootGroup; } else { - currentGroup = database.getPwDatabase().getGroupById(pwGroupId); + currentGroup = database.getGroupById(pwGroupId); } return currentGroup; @@ -1058,10 +1057,9 @@ public class GroupActivity extends LockingActivity if (actionNodeValues.getOldNode() != null) { PwGroupInterface parent = actionNodeValues.getOldNode().getParent(); - Database db = App.getDB(); - PwDatabase database = db.getPwDatabase(); - if (db.isRecycleBinAvailable() && - db.isRecycleBinEnabled()) { + Database database = App.getDB(); + if (database.isRecycleBinAvailable() && + database.isRecycleBinEnabled()) { PwGroupInterface recycleBin = database.getRecycleBin(); // Add trash if it doesn't exists if (parent.equals(recycleBin) @@ -1126,7 +1124,7 @@ public class GroupActivity extends LockingActivity } )); // Show the progress dialog now or after dialog confirmation - if (database.getPwDatabase().validatePasswordEncoding(masterPassword)) { + if (database.validatePasswordEncoding(masterPassword)) { taskThread.start(); } else { new PasswordEncodingDialogHelper() diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java index fb58682c1..100365e95 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java @@ -19,9 +19,7 @@ import android.view.ViewGroup; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.adapters.NodeAdapter; -import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.database.SortNodeEnum; -import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.element.PwNodeInterface; import com.kunzisoft.keepass.dialogs.SortDialogFragment; @@ -213,7 +211,6 @@ public class ListNodesFragment extends StylishFragment implements case R.id.menu_sort: SortDialogFragment sortDialogFragment; - PwDatabase database = App.getDB().getPwDatabase(); /* // TODO Recycle bin bottom if (database.isRecycleBinAvailable() && database.isRecycleBinEnabled()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 7eb164d61..304189db7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -251,8 +251,7 @@ public class NodeAdapter extends RecyclerView.Adapter { if (subNode.getType().equals(PwNodeInterface.Type.ENTRY)) { PwEntryInterface entry = (PwEntryInterface) subNode; - if (database != null) - database.startManageEntry(entry); + database.startManageEntry(entry); holder.text.setText(PwEntryInterface.getVisualTitle(entry)); @@ -262,8 +261,7 @@ public class NodeAdapter extends RecyclerView.Adapter { holder.subText.setText(username); } - if (database != null) - database.stopManageEntry(entry); + database.stopManageEntry(entry); } // Assign image and text size @@ -351,8 +349,10 @@ public class NodeAdapter extends RecyclerView.Adapter { MenuItem menuItem = contextMenu.findItem(R.id.menu_open); menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); + Database database = App.getDB(); + // Edition - if (readOnly || node.equals(App.getDB().getPwDatabase().getRecycleBin())) { + if (readOnly || node.equals(database.getRecycleBin())) { contextMenu.removeItem(R.id.menu_edit); } else { menuItem = contextMenu.findItem(R.id.menu_edit); @@ -362,7 +362,7 @@ public class NodeAdapter extends RecyclerView.Adapter { // Copy (not for group) if (readOnly || isASearchResult - || node.equals(App.getDB().getPwDatabase().getRecycleBin()) + || node.equals(database.getRecycleBin()) || node.getType().equals(PwNodeInterface.Type.GROUP)) { // TODO COPY For Group contextMenu.removeItem(R.id.menu_copy); @@ -374,7 +374,7 @@ public class NodeAdapter extends RecyclerView.Adapter { // Move if (readOnly || isASearchResult - || node.equals(App.getDB().getPwDatabase().getRecycleBin())) { + || node.equals(database.getRecycleBin())) { contextMenu.removeItem(R.id.menu_move); } else { menuItem = contextMenu.findItem(R.id.menu_move); @@ -382,7 +382,7 @@ public class NodeAdapter extends RecyclerView.Adapter { } // Deletion - if (readOnly || node.equals(App.getDB().getPwDatabase().getRecycleBin())) { + if (readOnly || node.equals(database.getRecycleBin())) { contextMenu.removeItem(R.id.menu_delete); } else { menuItem = contextMenu.findItem(R.id.menu_delete); diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java index ea2c45750..ca81259e3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java @@ -85,7 +85,7 @@ public class SearchEntryCursorAdapter extends CursorAdapter { // Retrieve elements from cursor UUID uuid = new UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))); - PwIconFactory iconFactory = database.getPwDatabase().getIconFactory(); + PwIconFactory iconFactory = database.getIconFactory(); PwIcon icon = iconFactory.getIcon( new UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt index 75b3ecc50..e0250bebe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt @@ -33,13 +33,13 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( withMasterPassword: Boolean, masterPassword: String?, withKeyFile: Boolean, - keyfile: Uri?, + keyFile: Uri?, actionRunnable: ActionRunnable? = null, save: Boolean) : SaveDatabaseRunnable(ctx, db, actionRunnable, save) { private var mMasterPassword: String? = null - private var mKeyfile: Uri? = null + private var mKeyFile: Uri? = null private var mBackupKey: ByteArray? = null @@ -47,19 +47,18 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( if (withMasterPassword) this.mMasterPassword = masterPassword if (withKeyFile) - this.mKeyfile = keyfile + this.mKeyFile = keyFile } override fun run() { // Set key try { - val pm = database.pwDatabase // TODO move master key methods - mBackupKey = ByteArray(pm.getMasterKey().size) - System.arraycopy(pm.getMasterKey(), 0, mBackupKey!!, 0, mBackupKey!!.size) + mBackupKey = ByteArray(database.masterKey.size) + System.arraycopy(database.masterKey, 0, mBackupKey!!, 0, mBackupKey!!.size) - val uriInputStream = UriUtil.getUriInputStream(context, mKeyfile) - pm.retrieveMasterKey(mMasterPassword, uriInputStream) + val uriInputStream = UriUtil.getUriInputStream(context, mKeyFile) + database.retrieveMasterKey(mMasterPassword, uriInputStream) // To save the database super.run() finishRun(true) @@ -75,8 +74,8 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( override fun onFinishRun(isSuccess: Boolean, message: String?) { if (!isSuccess) { // Erase the current master key - erase(database.pwDatabase.getMasterKey()) - database.pwDatabase.setMasterKey(mBackupKey) + erase(database.masterKey) + database.masterKey = mBackupKey } super.onFinishRun(isSuccess, message) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 6421b419b..a8201b740 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -34,6 +34,7 @@ import com.kunzisoft.keepass.database.cursor.EntryCursorV3; import com.kunzisoft.keepass.database.cursor.EntryCursorV4; import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException; import com.kunzisoft.keepass.database.exception.InvalidDBException; +import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.database.load.Importer; import com.kunzisoft.keepass.database.load.ImporterFactory; @@ -109,10 +110,6 @@ public class Database { return filename.substring(0, lastExtDot); } - public PwDatabase getPwDatabase() { - return pwDatabase; - } - public void setUri(Uri mUri) { this.mUri = mUri; } @@ -229,7 +226,7 @@ public class Database { } public Cursor searchEntry(String query) { - PwVersion version = getPwDatabase().getVersion(); + PwVersion version = pwDatabase.getVersion(); switch (version) { case V3: EntryCursorV3 cursorV3 = new EntryCursorV3(); @@ -262,10 +259,10 @@ public class Database { } public PwEntryInterface getEntryFrom(Cursor cursor) { - PwIconFactory iconFactory = getPwDatabase().getIconFactory(); + PwIconFactory iconFactory = pwDatabase.getIconFactory(); PwEntryInterface pwEntry = createEntry(); try { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V3: ((EntryCursorV3) cursor).populateEntry((PwEntryV3) pwEntry, iconFactory); break; @@ -358,11 +355,11 @@ public class Database { } public String getVersion() { - return getPwDatabase().getVersion().toString(); + return pwDatabase.getVersion().toString(); } public boolean containsName() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { default: return false; case V4: @@ -371,18 +368,18 @@ public class Database { } public String getName() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { default: return ""; case V4: - return ((PwDatabaseV4) getPwDatabase()).getName(); + return ((PwDatabaseV4) pwDatabase).getName(); } } public void assignName(String name) { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - PwDatabaseV4 databaseV4 = ((PwDatabaseV4) getPwDatabase()); + PwDatabaseV4 databaseV4 = ((PwDatabaseV4) pwDatabase); databaseV4.setName(name); databaseV4.setNameChanged(new PwDate()); break; @@ -390,7 +387,7 @@ public class Database { } public boolean containsDescription() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { default: return false; case V4: @@ -399,45 +396,45 @@ public class Database { } public String getDescription() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { default: return ""; case V4: - return ((PwDatabaseV4) getPwDatabase()).getDescription(); + return ((PwDatabaseV4) pwDatabase).getDescription(); } } public void assignDescription(String description) { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - ((PwDatabaseV4) getPwDatabase()).setDescription(description); - ((PwDatabaseV4) getPwDatabase()).setDescriptionChanged(new PwDate()); + ((PwDatabaseV4) pwDatabase).setDescription(description); + ((PwDatabaseV4) pwDatabase).setDescriptionChanged(new PwDate()); } } public String getDefaultUsername() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { default: return ""; case V4: - return ((PwDatabaseV4) getPwDatabase()).getDefaultUserName(); + return ((PwDatabaseV4) pwDatabase).getDefaultUserName(); } } public void setDefaultUsername(String username) { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - ((PwDatabaseV4) getPwDatabase()).setDefaultUserName(username); - ((PwDatabaseV4) getPwDatabase()).setDefaultUserNameChanged(new PwDate()); + ((PwDatabaseV4) pwDatabase).setDefaultUserName(username); + ((PwDatabaseV4) pwDatabase).setDefaultUserNameChanged(new PwDate()); } } public PwEncryptionAlgorithm getEncryptionAlgorithm() { - return getPwDatabase().getEncryptionAlgorithm(); + return pwDatabase.getEncryptionAlgorithm(); } public List getAvailableEncryptionAlgorithms() { - return getPwDatabase().getAvailableEncryptionAlgorithms(); + return pwDatabase.getAvailableEncryptionAlgorithms(); } public boolean allowEncryptionAlgorithmModification() { @@ -445,20 +442,20 @@ public class Database { } public void assignEncryptionAlgorithm(PwEncryptionAlgorithm algorithm) { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - ((PwDatabaseV4) getPwDatabase()).setEncryptionAlgorithm(algorithm); - ((PwDatabaseV4) getPwDatabase()).setDataEngine(algorithm.getCipherEngine()); - ((PwDatabaseV4) getPwDatabase()).setDataCipher(algorithm.getDataCipher()); + ((PwDatabaseV4) pwDatabase).setEncryptionAlgorithm(algorithm); + ((PwDatabaseV4) pwDatabase).setDataEngine(algorithm.getCipherEngine()); + ((PwDatabaseV4) pwDatabase).setDataCipher(algorithm.getDataCipher()); } } public String getEncryptionAlgorithmName(Resources resources) { - return getPwDatabase().getEncryptionAlgorithm().getName(resources); + return pwDatabase.getEncryptionAlgorithm().getName(resources); } public List getAvailableKdfEngines() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: return KdfFactory.kdfListV4; case V3: @@ -472,9 +469,9 @@ public class Database { } public KdfEngine getKdfEngine() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - KdfEngine kdfEngine = ((PwDatabaseV4) getPwDatabase()).getKdfEngine(); + KdfEngine kdfEngine = ((PwDatabaseV4) pwDatabase).getKdfEngine(); if (kdfEngine == null) return KdfFactory.aesKdf; return kdfEngine; @@ -485,9 +482,9 @@ public class Database { } public void assignKdfEngine(KdfEngine kdfEngine) { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - PwDatabaseV4 db = ((PwDatabaseV4) getPwDatabase()); + PwDatabaseV4 db = ((PwDatabaseV4) pwDatabase); if (db.getKdfParameters() == null || !db.getKdfParameters().getUUID().equals(kdfEngine.getDefaultParameters().getUUID())) db.setKdfParameters(kdfEngine.getDefaultParameters()); @@ -511,11 +508,11 @@ public class Database { } public long getNumberKeyEncryptionRounds() { - return getPwDatabase().getNumberKeyEncryptionRounds(); + return pwDatabase.getNumberKeyEncryptionRounds(); } public void setNumberKeyEncryptionRounds(long numberRounds) throws NumberFormatException { - getPwDatabase().setNumberKeyEncryptionRounds(numberRounds); + pwDatabase.setNumberKeyEncryptionRounds(numberRounds); } public String getMemoryUsageAsString() { @@ -523,17 +520,17 @@ public class Database { } public long getMemoryUsage() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - return ((PwDatabaseV4) getPwDatabase()).getMemoryUsage(); + return ((PwDatabaseV4) pwDatabase).getMemoryUsage(); } return KdfEngine.UNKNOW_VALUE; } public void setMemoryUsage(long memory) { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - ((PwDatabaseV4) getPwDatabase()).setMemoryUsage(memory); + ((PwDatabaseV4) pwDatabase).setMemoryUsage(memory); } } @@ -542,23 +539,44 @@ public class Database { } public int getParallelism() { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - return ((PwDatabaseV4) getPwDatabase()).getParallelism(); + return ((PwDatabaseV4) pwDatabase).getParallelism(); } return KdfEngine.UNKNOW_VALUE; } public void setParallelism(int parallelism) { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V4: - ((PwDatabaseV4) getPwDatabase()).setParallelism(parallelism); + ((PwDatabaseV4) pwDatabase).setParallelism(parallelism); } } + public boolean validatePasswordEncoding(String key) { + return pwDatabase.validatePasswordEncoding(key); + } + + public byte[] getMasterKey() { + return pwDatabase.getMasterKey(); + } + + public void setMasterKey(byte[] masterKey) { + pwDatabase.masterKey = masterKey; + } + + public void retrieveMasterKey(String key, InputStream keyInputStream) + throws InvalidKeyFileException, IOException { + pwDatabase.retrieveMasterKey(key, keyInputStream); + } + + public PwGroupInterface getRootGroup() { + return pwDatabase.getRootGroup(); + } + public PwEntryInterface createEntry() { try { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V3: return new PwEntryV3(); case V4: @@ -573,7 +591,7 @@ public class Database { public PwGroupInterface createGroup() { PwGroupInterface newPwGroup = null; try { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V3: newPwGroup = new PwGroupV3(); case V4: @@ -587,16 +605,16 @@ public class Database { } public PwEntryInterface getEntryById(PwNodeId id) { - return pwDatabase.entryIndexes.get(id); + return pwDatabase.getEntryById(id); } public PwGroupInterface getGroupById(PwNodeId id) { - return pwDatabase.groupIndexes.get(id); + return pwDatabase.getGroupById(id); } public void addEntryTo(PwEntryInterface entry, PwGroupInterface parent) { try { - getPwDatabase().addEntryTo(entry, parent); + pwDatabase.addEntryTo(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be added from this version of PwGroup", e); } @@ -604,7 +622,7 @@ public class Database { public void removeEntryFrom(PwEntryInterface entry, PwGroupInterface parent) { try { - getPwDatabase().removeEntryFrom(entry, parent); + pwDatabase.removeEntryFrom(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be removed from this version of PwGroup", e); } @@ -612,7 +630,7 @@ public class Database { public void addGroupTo(PwGroupInterface group, PwGroupInterface parent) { try { - getPwDatabase().addGroupTo(group, parent); + pwDatabase.addGroupTo(group, parent); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be added in this version of PwGroup", e); } @@ -620,7 +638,7 @@ public class Database { public void removeGroupFrom(PwGroupInterface group, PwGroupInterface parent) { try { - getPwDatabase().removeGroupFrom(group, parent); + pwDatabase.removeGroupFrom(group, parent); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be removed from this version of PwGroup", e); } @@ -628,7 +646,7 @@ public class Database { public boolean canRecycle(PwEntryInterface entry) { try { - getPwDatabase().canRecycle(entry); + pwDatabase.canRecycle(entry); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be recycled", e); } @@ -637,7 +655,7 @@ public class Database { public boolean canRecycle(PwGroupInterface group) { try { - getPwDatabase().canRecycle(group); + pwDatabase.canRecycle(group); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be recycled", e); } @@ -646,7 +664,7 @@ public class Database { public void recycle(PwEntryInterface entry) { try { - getPwDatabase().recycle(entry); + pwDatabase.recycle(entry); } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be recycled", e); } @@ -654,7 +672,7 @@ public class Database { public void recycle(PwGroupInterface group) { try { - getPwDatabase().recycle(group); + pwDatabase.recycle(group); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be recycled", e); } @@ -662,7 +680,7 @@ public class Database { public void updateEntry(PwEntryInterface oldEntry, PwEntryInterface newEntry) { try { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V3: ((PwEntryV3) oldEntry).updateWith((PwEntryV3) newEntry); break; @@ -677,7 +695,7 @@ public class Database { public void updateGroup(PwGroupInterface oldGroup, PwGroupInterface newGroup) { try { - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V3: ((PwGroupV3) oldGroup).updateWith((PwGroupV3) newGroup); break; @@ -698,7 +716,7 @@ public class Database { public @Nullable PwEntryInterface copyEntry(PwEntryInterface entryToCopy, PwGroupInterface newParent) { try { PwEntryInterface entryCopied = null; - switch (getPwDatabase().getVersion()) { + switch (pwDatabase.getVersion()) { case V3: entryCopied = ((PwEntryV3) entryToCopy).clone(); break; @@ -759,16 +777,24 @@ public class Database { } public boolean isRecycleBinAvailable() { - return getPwDatabase().isRecycleBinAvailable(); + return pwDatabase.isRecycleBinAvailable(); } public boolean isRecycleBinEnabled() { - return getPwDatabase().isRecycleBinEnabled(); + return pwDatabase.isRecycleBinEnabled(); } + public PwGroupInterface getRecycleBin() { + switch (pwDatabase.getVersion()) { + case V4: + return ((PwDatabaseV4) pwDatabase).getRecycleBin(); + } + return null; + } + public void undoRecycle(PwEntryInterface entry, PwGroupInterface parent) { try { - getPwDatabase().undoRecycle(entry, parent); + pwDatabase.undoRecycle(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo Recycle of this version of PwEntry", e); } @@ -776,7 +802,7 @@ public class Database { public void undoRecycle(PwGroupInterface group, PwGroupInterface parent) { try { - getPwDatabase().undoRecycle(group, parent); + pwDatabase.undoRecycle(group, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo Recycle of this version of PwGroup", e); } @@ -784,7 +810,7 @@ public class Database { public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface parent) { try { - getPwDatabase().undoDeleteEntryFrom(entry, parent); + pwDatabase.undoDeleteEntryFrom(entry, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo the deletion of this version of PwEntry", e); } @@ -792,33 +818,39 @@ public class Database { public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface parent) { try { - getPwDatabase().undoDeleteGroup(group, parent); + pwDatabase.undoDeleteGroup(group, parent); } catch (Exception e) { Log.e(TAG, "This version of database can't undo the deletion of this version of PwGroup", e); } } public void startManageEntry(PwEntryInterface entry) { - switch (getPwDatabase().getVersion()) { - case V4: - ((PwEntryV4) entry).startToManageFieldReferences((PwDatabaseV4) getPwDatabase()); - break; + if (pwDatabase != null) { + switch (pwDatabase.getVersion()) { + case V4: + ((PwEntryV4) entry).startToManageFieldReferences((PwDatabaseV4) pwDatabase); + break; + } } } public void stopManageEntry(PwEntryInterface entry) { - switch (getPwDatabase().getVersion()) { - case V4: - ((PwEntryV4) entry).stopToManageFieldReferences(); - break; + if (pwDatabase != null) { + switch (pwDatabase.getVersion()) { + case V4: + ((PwEntryV4) entry).stopToManageFieldReferences(); + break; + } } } public void createBackupOf(PwEntryInterface entry) { - switch (getPwDatabase().getVersion()) { - case V4: - ((PwEntryV4) entry).createBackup((PwDatabaseV4) getPwDatabase()); - break; + if (pwDatabase != null) { + switch (pwDatabase.getVersion()) { + case V4: + ((PwEntryV4) entry).createBackup((PwDatabaseV4) pwDatabase); + break; + } } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index f137815c7..da052d261 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -71,7 +71,8 @@ public abstract class PwDatabase { return finalKey; } - public abstract byte[] getMasterKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException; + protected abstract byte[] getMasterKey(String key, InputStream keyInputStream) + throws InvalidKeyFileException, IOException; public void retrieveMasterKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException { @@ -412,10 +413,6 @@ public abstract class PwDatabase { throw new RuntimeException("Call not valid for .kdb databases."); } - public PwGroupInterface getRecycleBin() { - return null; - } - public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { return group != null; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 066d952c2..bb237c919 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -659,7 +659,6 @@ public class PwDatabaseV4 extends PwDatabase { deletedObjects.remove(new PwDeletedObject((UUID) entry.getNodeId().getId())); } - @Override public PwGroupInterface getRecycleBin() { // TODO delete recycle bin preference if (recycleBinUUID == null) { return null; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index 4a0d75621..ec94f31dc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -49,7 +49,7 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo // TODO better technique ? try { PwNodeId pwGroupId = in.readParcelable(PwNodeId.class.getClassLoader()); - parent = App.getDB().getPwDatabase().getGroupById(pwGroupId); + parent = App.getDB().getGroupById(pwGroupId); } catch (Exception e) { e.printStackTrace(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index 3a5353157..236098279 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -121,7 +121,7 @@ public class GroupEditDialogFragment extends DialogFragment database = App.getDB(); editGroupDialogAction = EditGroupDialogAction.NONE; nameGroup = ""; - iconGroup = database.getPwDatabase().getIconFactory().getFolderIcon(); + iconGroup = database.getIconFactory().getFolderIcon(); if (savedInstanceState != null && savedInstanceState.containsKey(KEY_ACTION_ID) From 5705d367ed28e73f70d0c2238c5dbfbd2fbca313 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 13 Apr 2019 18:03:31 +0200 Subject: [PATCH 096/289] Fix UI thread for ProgressDialogFragment --- .../keepass/tasks/ProgressTaskDialogFragment.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt index 647f39622..2430a00ab 100644 --- a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt @@ -88,11 +88,13 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater { } private fun updateView(textView: TextView?, @StringRes resId: Int) { - if (resId == UNDEFINED) { - textView?.visibility = View.GONE - } else { - textView?.setText(resId) - textView?.visibility = View.VISIBLE + activity?.runOnUiThread { + if (resId == UNDEFINED) { + textView?.visibility = View.GONE + } else { + textView?.setText(resId) + textView?.visibility = View.VISIBLE + } } } From 6851d4a1d351b206462e1944bea1e95ad2865bcc Mon Sep 17 00:00:00 2001 From: Tobias Johansson Date: Wed, 17 Apr 2019 12:05:51 +0000 Subject: [PATCH 097/289] Translated using Weblate (Swedish) Currently translated at 21.2% (73 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sv/ --- app/src/main/res/values-sv/strings.xml | 345 ++++++++++++------------- 1 file changed, 172 insertions(+), 173 deletions(-) diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index bfb18afb9..d72cdbedc 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1,173 +1,172 @@ - - - - Feedback: - Hemsida: - KeePass DX är en Android-implementation av KeePass password manager. - OK - Ny post - Ny grupp - Algoritm - Tidsgräns för applikation - Tid innan låsning när applikationen är inaktiv. - Applikation - Inställningar för applikation - Visa inte igen - Parenteser - Filhantering kräver Open Intents File Manager, klicka nedan för att installera. Filhanteraren kanske inte fungerar korrekt vid första användningen. - Avbryt - Urklippet är rensat. - Urklippsfel - Vissa Samsung-telefoner har en bugg som gör att applikationer inte kan kopiera till urklipp. För mer detaljer, gå till: - Urklippet kunde inte rensas - Tidsgräns för urklipp - Tiden innan användarnamn och lösenord rensas från urklipp - Kopiera %1$s till urklipp - Skapar databasnyckel… - Databas - Dekrypterar databasinnehåll… - Använda denna databasen som standard - Siffror - KeePass DX \u00A9 %1$d Kunzisoft kommer HELT UTAN GARANTIER; Detta är fri programvara och du är välkommen att distribuera den utifrån villkoren i GPL version 3 eller senare. - Ange databasnamn - Senast använd - Avbryt - Kommentarer - Bekräfta lösenord - Skapad - Upphör att gälla - Nyckelfil - Senast ändrad - Lösenord - Spara - Namn - URL - Användarnamn - Strömchiffret ArcFour stöds inte. - KeePass DX kan inte hantera denna URI. - Kunde inte skapa föräldermapp. - Filen existerar redan. - Kunde inte öppna länk. - Ett filnamn krävs. - Kunde inte skapa filen: - Ogiltig databas. - Ogiltig sökväg. - Ett namn krävs. - Ett lösenord eller en nyckelfil är ett krav. - The phone ran out of memory while parsing your database. It may be too large for your phone. - At least one password generation type must be selected - Lösenorden matchar inte. - Antalet rundor måste vara en siffra. - Antalet rundor är för stort. Sätter värdet till 2147483648. - Ett fältnamn krävs för varje sträng. - En titel krävs. - Ange ett positivt heltal i fältet för längd - Fältnamn - Fältvärde - Filen hittades inte. - Filhanterare - Generera lösenord - bekräfta lösenord - genererat lösenord - Gruppnamn - nyckelfil - längd - lösenord - Lösenord - Installera från Play Store - Installera från F-Droid - Ogiltigt lösenord eller nyckelfil. - Ogiltig algoritm. - Databasformatet är okänt. - Nyckelfilen existerar inte. - Nyckelfilen är tom. - Längd - Storlek på grupplista - Textstorlek i grupplistan - Laddar databas… - Gemener - Maskera lösenord - Döljer lösenord som standard - Om - Byt nyckelfil - Inställningar - Databasinställningar - Radera - Donera - Redigera - Göm lösenord - Lås databas - Öppna - Sök - Visa lösenord - Gå till URL - Bindestreck - Aldrig - Inget sökresultat - Inget standardprogram för denna URL. - Senat öppnade databaser : - Sök inte efter poster i backup/papperskorg - Söker inte efter poster i \'Backup\' eller \'Recycle Bin\' - Skapar ny databas… - Arbetar… - Skydd - Sparar sökvägar till nyckelfiler - Spara nyckelfil - Ta bort - Rijndael (AES) - Root - Krypteringsrundor - Högre antal krypteringsrundor ger ytterligare skydd mot "brute force"-attacker, men kan göra att ladda och spara går betydligt långsammare. - rundor - Sparar databas… - Mellanslag - Sök - Sortera efter databasordning - Specialtecken - Postens titel/beskrivning - Sökresultat - Twofish - Understreck - Databasversionen stöds ej. - Versaler - SD-kortet är för närvarande inte monterat på enheten. Du kan inte skapa/ladda databasen. - Version %1$s - - Ange lösenord och/eller nyckelfil för att öppna databasen. - - - 5 sekunder - 10 sekunder - 20 sekunder - 30 sekunder - 1 minut - 5 minuter - 15 minuter - 30 minuter - Aldrig - - - Liten - Medium - Stor - - + + + Feedback: + Hemsida: + KeePass DX är en Android-implementation av KeePass password manager. + OK + Ny post + Ny grupp + Krypterings algoritm + Tidsgräns för applikation + Tid innan låsning när applikationen är inaktiv. + Applikation + Inställningar för applikation + Visa inte igen + Parenteser + Filhantering kräver Open Intents File Manager, klicka nedan för att installera. Filhanteraren kanske inte fungerar korrekt vid första användningen. + Avbryt + Urklippet är rensat. + Urklippsfel + Vissa Samsung-telefoner har en bugg som gör att applikationer inte kan kopiera till urklipp. För mer detaljer, gå till: + Urklippet kunde inte rensas + Tidsgräns för urklipp + Tiden innan användarnamn och lösenord rensas från urklipp + Kopiera %1$s till urklipp + Skapar databasnyckel… + Databas + Dekrypterar databasinnehåll… + Använda denna databasen som standard + Siffror + KeePass DX \u00A9 %1$d Kunzisoft kommer HELT UTAN GARANTIER; Detta är fri programvara och du är välkommen att distribuera den utifrån villkoren i GPL version 3 eller senare. + Ange databasnamn + Senast använd + Avbryt + Kommentarer + Bekräfta lösenord + Skapad + Upphör att gälla + Nyckelfil + Senast ändrad + Lösenord + Spara + Namn + URL + Användarnamn + Strömchiffret ArcFour stöds inte. + KeePass DX kan inte hantera denna URI. + Kunde inte skapa föräldermapp. + Filen existerar redan. + Kunde inte öppna länk. + Ett filnamn krävs. + Kunde inte skapa filen: + Ogiltig databas. + Ogiltig sökväg. + Ett namn krävs. + Ett lösenord eller en nyckelfil är ett krav. + The phone ran out of memory while parsing your database. It may be too large for your phone. + At least one password generation type must be selected + Lösenorden matchar inte. + Antalet rundor måste vara en siffra. + Antalet rundor är för stort. Sätter värdet till 2147483648. + Ett fältnamn krävs för varje sträng. + En titel krävs. + Ange ett positivt heltal i fältet för längd + Fältnamn + Fältvärde + Filen hittades inte. + Filhanterare + Generera lösenord + bekräfta lösenord + genererat lösenord + Gruppnamn + nyckelfil + längd + lösenord + Lösenord + Installera från Play Store + Installera från F-Droid + Ogiltigt lösenord eller nyckelfil. + Ogiltig algoritm. + Databasformatet är okänt. + Nyckelfilen existerar inte. + Nyckelfilen är tom. + Längd + Storlek på grupplista + Textstorlek i grupplistan + Laddar databas… + Gemener + Maskera lösenord + Döljer lösenord som standard + Om + Byt nyckelfil + Inställningar + Databasinställningar + Radera + Donera + Redigera + Göm lösenord + Lås databas + Öppna + Sök + Visa lösenord + Gå till URL + Bindestreck + Aldrig + Inget sökresultat + Inget standardprogram för denna URL. + Senat öppnade databaser : + Sök inte efter poster i backup/papperskorg + Söker inte efter poster i \'Backup\' eller \'Recycle Bin\' + Skapar ny databas… + Arbetar… + Skydd + Sparar sökvägar till nyckelfiler + Spara nyckelfil + Ta bort + Rijndael (AES) + Root + Krypteringsrundor + Högre antal krypteringsrundor ger ytterligare skydd mot "brute force"-attacker, men kan göra att ladda och spara går betydligt långsammare. + rundor + Sparar databas… + Mellanslag + Sök + Sortera efter databasordning + Specialtecken + Postens titel/beskrivning + Sökresultat + Twofish + Understreck + Databasversionen stöds ej. + Versaler + SD-kortet är för närvarande inte monterat på enheten. Du kan inte skapa/ladda databasen. + Version %1$s + Ange lösenord och/eller nyckelfil för att öppna databasen. + + 5 sekunder + 10 sekunder + 20 sekunder + 30 sekunder + 1 minut + 5 minuter + 15 minuter + 30 minuter + Aldrig + + + Liten + Medium + Stor + + Uppdatera post + Kryptering + \ No newline at end of file From 9e79da0efc845a9bf8ed54d6426a354a19def6aa Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 24 Apr 2019 20:38:13 +0200 Subject: [PATCH 098/289] Refactor database elements --- .../keepass/database/element/Database.java | 382 +++++++++--------- .../keepass/database/element/PwDatabase.java | 61 --- .../database/element/PwDatabaseV3.java | 5 - .../database/element/PwDatabaseV4.java | 66 ++- .../keepass/database/element/PwEntryV3.java | 2 +- .../keepass/database/element/PwEntryV4.java | 2 +- .../keepass/database/element/PwGroup.java | 98 +++++ .../database/element/PwGroupInterface.java | 2 - .../keepass/database/element/PwGroupV3.java | 71 +--- .../keepass/database/element/PwGroupV4.java | 81 +--- .../keepass/database/element/PwNode.java | 2 +- .../database/element/PwNodeInterface.java | 2 + .../database/load/ImporterFactory.java | 32 +- .../database/search/SearchDbHelper.java | 32 +- .../settings/NestedSettingsFragment.java | 1 - 15 files changed, 347 insertions(+), 492 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index a8201b740..6cce4bcfe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -25,7 +25,6 @@ import android.database.Cursor; import android.net.Uri; import android.util.Log; import android.webkit.URLUtil; - import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; import com.kunzisoft.keepass.database.EntryHandler; @@ -44,28 +43,23 @@ import com.kunzisoft.keepass.icons.IconDrawableFactory; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.UriUtil; - import org.apache.commons.io.FileUtils; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.SyncFailedException; +import javax.annotation.Nullable; +import java.io.*; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; - public class Database { private static final String TAG = Database.class.getName(); private PwDatabase pwDatabase = null; + private PwDatabaseV3 pwDatabaseV3 = null; + private PwDatabaseV4 pwDatabaseV4 = null; + private PwVersion version = null; + private Uri mUri = null; private SearchDbHelper searchHelper = null; private boolean readOnly = false; @@ -80,14 +74,31 @@ public class Database { public Database(String databasePath) { // TODO Test with kdb extension if (isKDBExtension(databasePath)) { - this.pwDatabase = new PwDatabaseV3(); + this.pwDatabaseV3 = new PwDatabaseV3(); + + this.pwDatabase = pwDatabaseV3; } else { - PwDatabaseV4 databaseV4 = new PwDatabaseV4(); - databaseV4.setRootGroup( - new PwGroupV4(dbNameFromPath(databasePath), - databaseV4.getIconFactory().getFolderIcon()) - ); - this.pwDatabase = databaseV4; + PwGroupV4 groupV4 = new PwGroupV4(); + this.pwDatabaseV4 = new PwDatabaseV4(); + + groupV4.setTitle(dbNameFromPath(databasePath)); + groupV4.setIconStandard(pwDatabaseV4.getIconFactory().getFolderIcon()); + this.pwDatabaseV4.setRootGroup(groupV4); + + this.pwDatabase = pwDatabaseV4; + } + this.version = pwDatabase.getVersion(); + } + + private void retrieveDatabaseVersioned(PwDatabase pwDatabase) { + this.version = pwDatabase.getVersion(); + switch (version) { + case V3: + pwDatabaseV3 = (PwDatabaseV3) pwDatabase; + break; + case V4: + pwDatabaseV4 = (PwDatabaseV4) pwDatabase; + break; } } @@ -187,17 +198,11 @@ public class Database { bis.reset(); // Return to the start pwDatabase = databaseImporter.openDatabase(bis, password, keyFileInputStream, progressTaskUpdater); + retrieveDatabaseVersioned(pwDatabase); if ( pwDatabase != null ) { try { passwordEncodingError = !pwDatabase.validatePasswordEncoding(password); - switch (pwDatabase.getVersion()) { - case V3: - searchHelper = new SearchDbHelper.SearchDbHelperV3(ctx); - break; - case V4: - searchHelper = new SearchDbHelper.SearchDbHelperV4(ctx); - break; - } + searchHelper = new SearchDbHelper(ctx); loaded = true; } catch (Exception e) { Log.e(TAG, "Load can't be performed with this Database version", e); @@ -211,22 +216,12 @@ public class Database { } public PwGroupInterface search(String str, int max) { - if (searchHelper == null) { return null; } - try { - switch (pwDatabase.getVersion()) { - case V3: - return ((SearchDbHelper.SearchDbHelperV3) searchHelper).search(((PwDatabaseV3) pwDatabase), str, max); - case V4: - return ((SearchDbHelper.SearchDbHelperV4) searchHelper).search(((PwDatabaseV4) pwDatabase), str, max); - } - } catch (Exception e) { - Log.e(TAG, "Search can't be performed with this SearchHelper", e); - } - return null; + if (searchHelper == null) + return null; + return searchHelper.search(pwDatabase, str, max); } public Cursor searchEntry(String query) { - PwVersion version = pwDatabase.getVersion(); switch (version) { case V3: EntryCursorV3 cursorV3 = new EntryCursorV3(); @@ -262,7 +257,7 @@ public class Database { PwIconFactory iconFactory = pwDatabase.getIconFactory(); PwEntryInterface pwEntry = createEntry(); try { - switch (pwDatabase.getVersion()) { + switch (version) { case V3: ((EntryCursorV3) cursor).populateEntry((PwEntryV3) pwEntry, iconFactory); break; @@ -339,8 +334,8 @@ public class Database { public void closeAndClear(Context context) { drawFactory.clearCache(); // Delete the cache of the database if present - if (pwDatabase != null) - pwDatabase.clearCache(); + if (pwDatabaseV4 != null) + pwDatabaseV4.clearCache(); // In all cases, delete all the files in the temp dir try { FileUtils.cleanDirectory(context.getFilesDir()); @@ -349,17 +344,19 @@ public class Database { } pwDatabase = null; + pwDatabaseV3 = null; + pwDatabaseV4 = null; mUri = null; loaded = false; passwordEncodingError = false; } public String getVersion() { - return pwDatabase.getVersion().toString(); + return version.toString(); } public boolean containsName() { - switch (pwDatabase.getVersion()) { + switch (version) { default: return false; case V4: @@ -368,26 +365,25 @@ public class Database { } public String getName() { - switch (pwDatabase.getVersion()) { + switch (version) { default: return ""; case V4: - return ((PwDatabaseV4) pwDatabase).getName(); + return pwDatabaseV4.getName(); } } public void assignName(String name) { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - PwDatabaseV4 databaseV4 = ((PwDatabaseV4) pwDatabase); - databaseV4.setName(name); - databaseV4.setNameChanged(new PwDate()); + pwDatabaseV4.setName(name); + pwDatabaseV4.setNameChanged(new PwDate()); break; } } public boolean containsDescription() { - switch (pwDatabase.getVersion()) { + switch (version) { default: return false; case V4: @@ -396,36 +392,36 @@ public class Database { } public String getDescription() { - switch (pwDatabase.getVersion()) { + switch (version) { default: return ""; case V4: - return ((PwDatabaseV4) pwDatabase).getDescription(); + return pwDatabaseV4.getDescription(); } } public void assignDescription(String description) { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - ((PwDatabaseV4) pwDatabase).setDescription(description); - ((PwDatabaseV4) pwDatabase).setDescriptionChanged(new PwDate()); + pwDatabaseV4.setDescription(description); + pwDatabaseV4.setDescriptionChanged(new PwDate()); } } public String getDefaultUsername() { - switch (pwDatabase.getVersion()) { + switch (version) { default: return ""; case V4: - return ((PwDatabaseV4) pwDatabase).getDefaultUserName(); + return pwDatabaseV4.getDefaultUserName(); } } public void setDefaultUsername(String username) { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - ((PwDatabaseV4) pwDatabase).setDefaultUserName(username); - ((PwDatabaseV4) pwDatabase).setDefaultUserNameChanged(new PwDate()); + pwDatabaseV4.setDefaultUserName(username); + pwDatabaseV4.setDefaultUserNameChanged(new PwDate()); } } @@ -442,11 +438,11 @@ public class Database { } public void assignEncryptionAlgorithm(PwEncryptionAlgorithm algorithm) { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - ((PwDatabaseV4) pwDatabase).setEncryptionAlgorithm(algorithm); - ((PwDatabaseV4) pwDatabase).setDataEngine(algorithm.getCipherEngine()); - ((PwDatabaseV4) pwDatabase).setDataCipher(algorithm.getDataCipher()); + pwDatabaseV4.setEncryptionAlgorithm(algorithm); + pwDatabaseV4.setDataEngine(algorithm.getCipherEngine()); + pwDatabaseV4.setDataCipher(algorithm.getDataCipher()); } } @@ -455,7 +451,7 @@ public class Database { } public List getAvailableKdfEngines() { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: return KdfFactory.kdfListV4; case V3: @@ -469,9 +465,9 @@ public class Database { } public KdfEngine getKdfEngine() { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - KdfEngine kdfEngine = ((PwDatabaseV4) pwDatabase).getKdfEngine(); + KdfEngine kdfEngine = pwDatabaseV4.getKdfEngine(); if (kdfEngine == null) return KdfFactory.aesKdf; return kdfEngine; @@ -482,12 +478,11 @@ public class Database { } public void assignKdfEngine(KdfEngine kdfEngine) { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - PwDatabaseV4 db = ((PwDatabaseV4) pwDatabase); - if (db.getKdfParameters() == null - || !db.getKdfParameters().getUUID().equals(kdfEngine.getDefaultParameters().getUUID())) - db.setKdfParameters(kdfEngine.getDefaultParameters()); + if (pwDatabaseV4.getKdfParameters() == null + || !pwDatabaseV4.getKdfParameters().getUUID().equals(kdfEngine.getDefaultParameters().getUUID())) + pwDatabaseV4.setKdfParameters(kdfEngine.getDefaultParameters()); setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds()); setMemoryUsage(kdfEngine.getDefaultMemoryUsage()); setParallelism(kdfEngine.getDefaultParallelism()); @@ -520,17 +515,17 @@ public class Database { } public long getMemoryUsage() { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - return ((PwDatabaseV4) pwDatabase).getMemoryUsage(); + return pwDatabaseV4.getMemoryUsage(); } return KdfEngine.UNKNOW_VALUE; } public void setMemoryUsage(long memory) { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - ((PwDatabaseV4) pwDatabase).setMemoryUsage(memory); + pwDatabaseV4.setMemoryUsage(memory); } } @@ -539,17 +534,17 @@ public class Database { } public int getParallelism() { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - return ((PwDatabaseV4) pwDatabase).getParallelism(); + return pwDatabaseV4.getParallelism(); } return KdfEngine.UNKNOW_VALUE; } public void setParallelism(int parallelism) { - switch (pwDatabase.getVersion()) { + switch (version) { case V4: - ((PwDatabaseV4) pwDatabase).setParallelism(parallelism); + pwDatabaseV4.setParallelism(parallelism); } } @@ -576,7 +571,7 @@ public class Database { public PwEntryInterface createEntry() { try { - switch (pwDatabase.getVersion()) { + switch (version) { case V3: return new PwEntryV3(); case V4: @@ -591,7 +586,7 @@ public class Database { public PwGroupInterface createGroup() { PwGroupInterface newPwGroup = null; try { - switch (pwDatabase.getVersion()) { + switch (version) { case V3: newPwGroup = new PwGroupV3(); case V4: @@ -644,43 +639,9 @@ public class Database { } } - public boolean canRecycle(PwEntryInterface entry) { - try { - pwDatabase.canRecycle(entry); - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be recycled", e); - } - return false; - } - - public boolean canRecycle(PwGroupInterface group) { - try { - pwDatabase.canRecycle(group); - } catch (Exception e) { - Log.e(TAG, "This version of PwGroup can't be recycled", e); - } - return false; - } - - public void recycle(PwEntryInterface entry) { - try { - pwDatabase.recycle(entry); - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be recycled", e); - } - } - - public void recycle(PwGroupInterface group) { - try { - pwDatabase.recycle(group); - } catch (Exception e) { - Log.e(TAG, "This version of PwGroup can't be recycled", e); - } - } - public void updateEntry(PwEntryInterface oldEntry, PwEntryInterface newEntry) { try { - switch (pwDatabase.getVersion()) { + switch (version) { case V3: ((PwEntryV3) oldEntry).updateWith((PwEntryV3) newEntry); break; @@ -695,7 +656,7 @@ public class Database { public void updateGroup(PwGroupInterface oldGroup, PwGroupInterface newGroup) { try { - switch (pwDatabase.getVersion()) { + switch (version) { case V3: ((PwGroupV3) oldGroup).updateWith((PwGroupV3) newGroup); break; @@ -716,12 +677,12 @@ public class Database { public @Nullable PwEntryInterface copyEntry(PwEntryInterface entryToCopy, PwGroupInterface newParent) { try { PwEntryInterface entryCopied = null; - switch (pwDatabase.getVersion()) { + switch (version) { case V3: - entryCopied = ((PwEntryV3) entryToCopy).clone(); + entryCopied = entryToCopy.duplicate(); break; case V4: - entryCopied = ((PwEntryV4) entryToCopy).clone(); + entryCopied = entryToCopy.duplicate(); break; } entryCopied.setNodeId(new PwNodeIdUUID()); @@ -745,98 +706,143 @@ public class Database { } public void deleteEntry(PwEntryInterface entry) { - try { - PwGroupInterface parent = entry.getParent(); - removeEntryFrom(entry, parent); - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be deleted", e); - } + removeEntryFrom(entry, entry.getParent()); } public void deleteGroup(PwGroupInterface group) { - try { - PwGroupInterface.doForEachChildAndForRoot(group, - new EntryHandler() { - @Override - public boolean operate(PwEntryInterface entry) { - deleteEntry(entry); - return true; - } - }, - new GroupHandler() { - @Override - public boolean operate(PwGroupInterface group) { - PwGroupInterface parent = group.getParent(); - removeGroupFrom(group, parent); - return true; - } - }); - } catch (Exception e) { - Log.e(TAG, "This version of PwGroup can't be deleted", e); - } + PwGroupInterface.doForEachChildAndForRoot(group, + new EntryHandler() { + @Override + public boolean operate(PwEntryInterface entry) { + deleteEntry(entry); + return true; + } + }, + new GroupHandler() { + @Override + public boolean operate(PwGroupInterface group) { + PwGroupInterface parent = group.getParent(); + removeGroupFrom(group, parent); + return true; + } + }); } + public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface parent) { + pwDatabase.undoDeleteEntryFrom(entry, parent); + } + + public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface parent) { + pwDatabase.undoDeleteGroup(group, parent); + } + + /** + * Determine if RecycleBin is available or not for this version of database + * @return true if RecycleBin available + */ public boolean isRecycleBinAvailable() { - return pwDatabase.isRecycleBinAvailable(); + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + return true; + } + } + return false; } public boolean isRecycleBinEnabled() { - return pwDatabase.isRecycleBinEnabled(); + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + return pwDatabaseV4.isRecycleBinEnabled(); + } + } + return false; } public PwGroupInterface getRecycleBin() { - switch (pwDatabase.getVersion()) { - case V4: - return ((PwDatabaseV4) pwDatabase).getRecycleBin(); - } + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + return pwDatabaseV4.getRecycleBin(); + } + } return null; } + public boolean canRecycle(PwEntryInterface entry) { + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + return pwDatabaseV4.canRecycle(entry); + } + } + return false; + } + + public boolean canRecycle(PwGroupInterface group) { + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + return pwDatabaseV4.canRecycle(group); + } + } + return false; + } + + public void recycle(PwEntryInterface entry) { + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + pwDatabaseV4.recycle(entry); + break; + } + } + } + + public void recycle(PwGroupInterface group) { + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + pwDatabaseV4.recycle(group); + break; + } + } + } + public void undoRecycle(PwEntryInterface entry, PwGroupInterface parent) { - try { - pwDatabase.undoRecycle(entry, parent); - } catch (Exception e) { - Log.e(TAG, "This version of database can't undo Recycle of this version of PwEntry", e); + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + pwDatabaseV4.undoRecycle(entry, parent); + break; + } } } public void undoRecycle(PwGroupInterface group, PwGroupInterface parent) { - try { - pwDatabase.undoRecycle(group, parent); - } catch (Exception e) { - Log.e(TAG, "This version of database can't undo Recycle of this version of PwGroup", e); - } - } - - public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface parent) { - try { - pwDatabase.undoDeleteEntryFrom(entry, parent); - } catch (Exception e) { - Log.e(TAG, "This version of database can't undo the deletion of this version of PwEntry", e); - } - } - - public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface parent) { - try { - pwDatabase.undoDeleteGroup(group, parent); - } catch (Exception e) { - Log.e(TAG, "This version of database can't undo the deletion of this version of PwGroup", e); + if (pwDatabaseV4 != null) { + switch (version) { + case V4: + pwDatabaseV4.undoRecycle(group, parent); + break; + } } } public void startManageEntry(PwEntryInterface entry) { - if (pwDatabase != null) { - switch (pwDatabase.getVersion()) { + if (pwDatabaseV4 != null) { + switch (version) { case V4: - ((PwEntryV4) entry).startToManageFieldReferences((PwDatabaseV4) pwDatabase); + ((PwEntryV4) entry).startToManageFieldReferences(pwDatabaseV4); break; } } } public void stopManageEntry(PwEntryInterface entry) { - if (pwDatabase != null) { - switch (pwDatabase.getVersion()) { + if (pwDatabaseV4 != null) { + switch (version) { case V4: ((PwEntryV4) entry).stopToManageFieldReferences(); break; @@ -845,10 +851,10 @@ public class Database { } public void createBackupOf(PwEntryInterface entry) { - if (pwDatabase != null) { - switch (pwDatabase.getVersion()) { + if (pwDatabaseV4 != null) { + switch (version) { case V4: - ((PwEntryV4) entry).createBackup((PwDatabaseV4) pwDatabase); + ((PwEntryV4) entry).createBackup(pwDatabaseV4); break; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index da052d261..3912fd8d4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -355,68 +355,7 @@ public abstract class PwDatabase { public abstract boolean isBackup(PwGroupInterface group); - /* - * ------------------------------------- - * RecycleBin - * ------------------------------------- - */ - - /** - * Determine if RecycleBin is available or not for this version of database - * @return true if RecycleBin enable - */ - protected boolean isRecycleBinAvailable() { - return false; - } - - /** - * Determine if RecycleBin is enable or not - * @return true if RecycleBin enable, false if is not available or not enable - */ - protected boolean isRecycleBinEnabled() { - return false; - } - - /** - * Define if a Group must be delete or recycle - * @param group Group to remove - * @return true if group can be recycle, false elsewhere - */ - protected boolean canRecycle(PwGroupInterface group) { - return false; - } - - /** - * Define if an Entry must be delete or recycle - * @param entry Entry to remove - * @return true if entry can be recycle, false elsewhere - */ - protected boolean canRecycle(PwEntryInterface entry) { - return false; - } - - protected void recycle(PwGroupInterface group) { - // Assume calls to this are protected by calling inRecyleBin - throw new RuntimeException("Call not valid for .kdb databases."); - } - - protected void recycle(PwEntryInterface entry) { - // Assume calls to this are protected by calling inRecyleBin - throw new RuntimeException("Call not valid for .kdb databases."); - } - - protected void undoRecycle(PwGroupInterface group, PwGroupInterface origParent) { - throw new RuntimeException("Call not valid for .kdb databases."); - } - - protected void undoRecycle(PwEntryInterface entry, PwGroupInterface origParent) { - throw new RuntimeException("Call not valid for .kdb databases."); - } - public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { return group != null; } - - public abstract void clearCache(); - } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index d8ce10a21..7f7fae7ad 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -177,7 +177,6 @@ public class PwDatabaseV3 extends PwDatabase { } group = group.getParent(); } - return false; } @@ -186,10 +185,6 @@ public class PwDatabaseV3 extends PwDatabase { if (!super.isGroupSearchable(group, omitBackup)) { return false; } - return !(omitBackup && isBackup(group)); } - - @Override - public void clearCache() {} } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index bb237c919..c4933f595 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.element; import android.util.Log; - +import biz.source_code.base64Coder.Base64Coder; import com.kunzisoft.keepass.collections.VariantDictionary; import com.kunzisoft.keepass.crypto.CryptoUtil; import com.kunzisoft.keepass.crypto.engine.AesEngine; @@ -28,37 +28,19 @@ import com.kunzisoft.keepass.crypto.engine.CipherEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; -import com.kunzisoft.keepass.database.BinaryPool; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; -import com.kunzisoft.keepass.database.MemoryProtectionConfig; -import com.kunzisoft.keepass.database.PwCompressionAlgorithm; +import com.kunzisoft.keepass.database.*; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.UnknownKDF; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.Text; - -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import org.w3c.dom.*; import javax.annotation.Nullable; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; - -import biz.source_code.base64Coder.Base64Coder; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; public class PwDatabaseV4 extends PwDatabase { @@ -536,7 +518,9 @@ public class PwDatabaseV4 extends PwDatabase { if (getRecycleBin() == null) { // Create recycle bin - PwGroupV4 recycleBin = new PwGroupV4(RECYCLEBIN_NAME, iconFactory.getTrashIcon()); + PwGroupV4 recycleBin = new PwGroupV4(); + recycleBin.setTitle(RECYCLEBIN_NAME); + recycleBin.setIconStandard(iconFactory.getTrashIcon()); recycleBin.setEnableAutoType(false); recycleBin.setEnableSearching(false); recycleBin.setExpanded(false); @@ -554,12 +538,10 @@ public class PwDatabaseV4 extends PwDatabase { this.recycleBinUUID = recycleBinUUID; } - @Override - public boolean isRecycleBinAvailable() { - return true; - } - - @Override + /** + * Determine if RecycleBin is enable or not + * @return true if RecycleBin enable, false if is not available or not enable + */ public boolean isRecycleBinEnabled() { return recycleBinEnabled; } @@ -577,7 +559,11 @@ public class PwDatabaseV4 extends PwDatabase { this.recycleBinChanged = recycleBinChanged; } - @Override + /** + * Define if a Group must be delete or recycle + * @param group Group to remove + * @return true if group can be recycle, false elsewhere + */ public boolean canRecycle(PwGroupInterface group) { if (!recycleBinEnabled) { return false; @@ -586,7 +572,11 @@ public class PwDatabaseV4 extends PwDatabase { return (recycle == null) || (!group.isContainedIn(recycle)); } - @Override + /** + * Define if an Entry must be delete or recycle + * @param entry Entry to remove + * @return true if entry can be recycle, false elsewhere + */ public boolean canRecycle(PwEntryInterface entry) { if (!recycleBinEnabled) { return false; @@ -595,7 +585,6 @@ public class PwDatabaseV4 extends PwDatabase { return (parent != null) && canRecycle(parent); } - @Override public void recycle(PwGroupInterface group) { ensureRecycleBin(); @@ -608,7 +597,6 @@ public class PwDatabaseV4 extends PwDatabase { // TODO ? group.touchLocation(); } - @Override public void recycle(PwEntryInterface entry) { ensureRecycleBin(); @@ -621,7 +609,6 @@ public class PwDatabaseV4 extends PwDatabase { entry.touchLocation(); } - @Override public void undoRecycle(PwGroupInterface group, PwGroupInterface origParent) { PwGroupInterface recycleBin = getRecycleBin(); @@ -630,7 +617,6 @@ public class PwDatabaseV4 extends PwDatabase { addGroupTo(group, origParent); } - @Override public void undoRecycle(PwEntryInterface entry, PwGroupInterface origParent) { PwGroupInterface recycleBin = getRecycleBin(); @@ -693,7 +679,6 @@ public class PwDatabaseV4 extends PwDatabase { if (!super.isGroupSearchable(group, omitBackup)) { return false; } - return group.isSearchingEnabled(); } @@ -702,7 +687,6 @@ public class PwDatabaseV4 extends PwDatabase { return true; } - @Override public void clearCache() { binPool.clear(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index d09ef347b..f2ca42a16 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -134,7 +134,7 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { }; public void updateWith(PwEntryV3 source) { - super.assign(source); + super.updateWith(source); title = source.title; username = source.username; int passLen = source.password.length; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 89217fde9..067d0e8ca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -122,7 +122,7 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte }; public void updateWith(PwEntryV4 source) { - super.assign(source); + super.updateWith(source); customIcon = source.customIcon; usageCount = source.usageCount; parentGroupLastMod = source.parentGroupLastMod; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java new file mode 100644 index 000000000..715447eeb --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java @@ -0,0 +1,98 @@ +package com.kunzisoft.keepass.database.element; + +import android.os.Parcel; + +import java.util.ArrayList; +import java.util.List; + +public abstract class PwGroup extends PwNode implements PwGroupInterface { + + private String title = ""; + transient private List childGroups = new ArrayList<>(); + transient private List childEntries = new ArrayList<>(); + + public PwGroup() { + super(); + } + + public PwGroup(Parcel in) { + super(in); + title = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(title); + } + + protected void updateWith(PwGroup source) { + super.updateWith(source); + title = source.title; + } + + @Override + public String getTitle() { + return title; + } + + @Override + public void setTitle(String name) { + this.title = name; + } + + @Override + public List getChildGroups() { + return childGroups; + } + + @Override + public List getChildEntries() { + return childEntries; + } + + @Override + public void addChildGroup(PwGroupInterface group) { + this.childGroups.add(group); + } + + @Override + public void addChildEntry(PwEntryInterface entry) { + this.childEntries.add(entry); + } + + @Override + public void removeChildGroup(PwGroupInterface group) { + this.childGroups.remove(group); + } + + @Override + public void removeChildEntry(PwEntryInterface entry) { + this.childEntries.remove(entry); + } + + @Override + public int getLevel() { + return -1; + } + + @Override + public void setLevel(int level) { + // Do nothing here + } + + @Override + public List getChildrenWithoutMetastream() { + List children = new ArrayList<>(childGroups); + for(PwEntryInterface child : childEntries) { + if (!child.isMetaStream()) + children.add(child); + } + return children; + } + + @Override + public String toString() { + return getTitle(); + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java index ffd47b045..add78a1df 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java @@ -25,8 +25,6 @@ public interface PwGroupInterface extends PwNodeInterface { void removeChildEntry(PwEntryInterface entry); - boolean containsParent(); - /** * Filter MetaStream entries and return children * @return List of direct children (one level below) as PwNode diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 1bbf4f1c3..76a29f3d7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -22,17 +22,8 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -import java.util.ArrayList; -import java.util.List; +public class PwGroupV3 extends PwGroup { -public class PwGroupV3 extends PwNode implements PwGroupInterface { - - // TODO verify children not needed - transient private List childGroups = new ArrayList<>(); - transient private List childEntries = new ArrayList<>(); - - // for tree traversing - private String title = ""; private int level = 0; // short /** Used by KeePass internally, don't use */ private int flags; @@ -48,7 +39,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { public PwGroupV3(Parcel in) { super(in); - title = in.readString(); level = in.readInt(); flags = in.readInt(); } @@ -56,7 +46,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeString(title); dest.writeInt(level); dest.writeInt(flags); } @@ -74,8 +63,7 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { }; protected void updateWith(PwGroupV3 source) { - super.assign(source); - title = source.title; + super.updateWith(source); level = source.level; flags = source.flags; } @@ -137,61 +125,6 @@ public class PwGroupV3 extends PwNode implements PwGroupInterface { this.flags = flags; } - @Override - public String toString() { - return getTitle(); - } - - @Override - public String getTitle() { - return title; - } - - @Override - public void setTitle(String title) { - this.title = title; - } - - @Override - public List getChildGroups() { - return childGroups; - } - - @Override - public List getChildEntries() { - return childEntries; - } - - @Override - public void addChildGroup(PwGroupInterface group) { - this.childGroups.add(group); - } - - @Override - public void addChildEntry(PwEntryInterface entry) { - this.childEntries.add(entry); - } - - @Override - public void removeChildGroup(PwGroupInterface group) { - this.childGroups.remove(group); - } - - @Override - public void removeChildEntry(PwEntryInterface entry) { - this.childEntries.remove(entry); - } - - @Override - public List getChildrenWithoutMetastream() { - List children = new ArrayList<>(childGroups); - for(PwEntryInterface child : childEntries) { - if (!child.isMetaStream()) - children.add(child); - } - return children; - } - @Override public boolean allowAddEntryIfIsRoot() { return false; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index f93729ac5..0e3a05c16 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -20,24 +20,16 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; - import com.kunzisoft.keepass.database.ITimeLogger; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.UUID; -public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInterface { +public class PwGroupV4 extends PwGroup implements ITimeLogger { public static final boolean DEFAULT_SEARCHING_ENABLED = true; - // TODO verify children not needed - transient private List childGroups = new ArrayList<>(); - transient private List childEntries = new ArrayList<>(); - - private String title = ""; private PwIconCustom customIcon = PwIconCustom.ZERO; private long usageCount = 0; private PwDate locationChangeDate = new PwDate(); @@ -58,16 +50,9 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter public PwGroupV4() { super(); } - - public PwGroupV4(String title, PwIconStandard icon) { - super(); - this.title = title; - this.icon = icon; - } public PwGroupV4(Parcel in) { super(in); - title = in.readString(); customIcon = in.readParcelable(PwIconCustom.class.getClassLoader()); usageCount = in.readLong(); locationChangeDate = in.readParcelable(PwDate.class.getClassLoader()); @@ -86,7 +71,6 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeString(title); dest.writeParcelable(customIcon, flags); dest.writeLong(usageCount); dest.writeParcelable(locationChangeDate, flags); @@ -113,8 +97,7 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter }; protected void updateWith(PwGroupV4 source) { - super.assign(source); - title = source.title; + super.updateWith(source); customIcon = source.customIcon; usageCount = source.usageCount; locationChangeDate = source.locationChangeDate; @@ -297,66 +280,6 @@ public class PwGroupV4 extends PwNode implements ITimeLogger, PwGroupInter return true; } - @Override - public String getTitle() { - return title; - } - - @Override - public void setTitle(String name) { - this.title = name; - } - - @Override - public List getChildGroups() { - return childGroups; - } - - @Override - public List getChildEntries() { - return childEntries; - } - - @Override - public int getLevel() { - return -1; // TODO Level - } - - @Override - public void setLevel(int level) { - // Do nothing here - } - - @Override - public void addChildGroup(PwGroupInterface group) { - this.childGroups.add(group); - } - - @Override - public void addChildEntry(PwEntryInterface entry) { - this.childEntries.add(entry); - } - - @Override - public void removeChildGroup(PwGroupInterface group) { - this.childGroups.remove(group); - } - - @Override - public void removeChildEntry(PwEntryInterface entry) { - this.childEntries.remove(entry); - } - - @Override - public List getChildrenWithoutMetastream() { - List children = new ArrayList<>(childGroups); - for(PwEntryInterface child : childEntries) { - if (!child.isMetaStream()) - children.add(child); - } - return children; - } - @Override public boolean allowAddEntryIfIsRoot() { return true; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index ec94f31dc..d3172c22a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -82,7 +82,7 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo return 0; } - protected void assign(PwNode source) { + protected void updateWith(PwNode source) { this.nodeId = source.nodeId; this.parent = source.parent; this.icon = source.icon; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java index 73e96f3c4..51b07977b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java @@ -37,6 +37,8 @@ public interface PwNodeInterface extends SmallTimeInterface, Parcelable { */ void setParent(PwGroupInterface prt); + boolean containsParent(); + void touch(boolean modified, boolean touchParents); boolean isContainedIn(PwGroupInterface container); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java index d2ebef187..6fdd902c8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java @@ -29,24 +29,20 @@ import java.io.IOException; import java.io.InputStream; public class ImporterFactory { - public static Importer createImporter(InputStream is, File streamDir) throws InvalidDBSignatureException, IOException { - return createImporter(is, streamDir,false); - } - public static Importer createImporter(InputStream is, File streamDir, boolean debug) throws InvalidDBSignatureException, IOException { - int sig1 = LEDataInputStream.readInt(is); - int sig2 = LEDataInputStream.readInt(is); - - if ( PwDbHeaderV3.matchesHeader(sig1, sig2) ) { - if (debug) { - return new ImporterV3Debug(); - } - - return new ImporterV3(); - } else if ( PwDbHeaderV4.matchesHeader(sig1, sig2) ) { - return new ImporterV4(streamDir); - } + public static Importer createImporter(InputStream is, File streamDir, boolean debug) throws InvalidDBSignatureException, IOException { + int sig1 = LEDataInputStream.readInt(is); + int sig2 = LEDataInputStream.readInt(is); - throw new InvalidDBSignatureException(); - } + if ( PwDbHeaderV3.matchesHeader(sig1, sig2) ) { + if (debug) { + return new ImporterV3Debug(); + } + return new ImporterV3(); + } else if ( PwDbHeaderV4.matchesHeader(sig1, sig2) ) { + return new ImporterV4(streamDir); + } + + throw new InvalidDBSignatureException(); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index 61ca893aa..aafdda43f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -22,13 +22,10 @@ package com.kunzisoft.keepass.database.search; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; - import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.database.EntryHandler; import com.kunzisoft.keepass.database.GroupHandler; import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwDatabaseV4; import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; @@ -36,21 +33,21 @@ import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import java.util.Iterator; import java.util.Locale; -public class SearchDbHelper { +public class SearchDbHelper { - private final Context mCtx; + private final Context mContext; private int incrementEntry = 0; - public SearchDbHelper(Context ctx) { - this.mCtx = ctx; + public SearchDbHelper(Context context) { + this.mContext = context; } private boolean omitBackup() { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mCtx); - return prefs.getBoolean(mCtx.getString(R.string.omitbackup_key), mCtx.getResources().getBoolean(R.bool.omitbackup_default)); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + return prefs.getBoolean(mContext.getString(R.string.omitbackup_key), mContext.getResources().getBoolean(R.bool.omitbackup_default)); } - public PwGroupInterface search(PwDatabaseVersion pm, String qStr, int max) { + public PwGroupInterface search(PwDatabase pm, String qStr, int max) { PwGroupInterface searchGroup = pm.createGroup(); searchGroup.setTitle("\"" + qStr + "\""); @@ -101,19 +98,4 @@ public class SearchDbHelper { } return false; } - - public static class SearchDbHelperV3 extends SearchDbHelper{ - - public SearchDbHelperV3(Context ctx) { - super(ctx); - } - } - - public static class SearchDbHelperV4 extends SearchDbHelper{ - - public SearchDbHelperV4(Context ctx) { - super(ctx); - } - } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index a700dc810..a42c59429 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -347,7 +347,6 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat // TODO Recycle dbGeneralPrefCategory.removePreference(recycleBinPref); // To delete if (database.isRecycleBinAvailable()) { - recycleBinPref.setChecked(database.isRecycleBinEnabled()); recycleBinPref.setEnabled(false); } else { From e460a4fe4fb26b6b460bce4e9cd482f192a2c302 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 24 Apr 2019 21:40:28 +0200 Subject: [PATCH 099/289] Refactor database import --- .../keepass/tests/database/Kdb3.java | 13 +-- .../keepass/tests/database/Kdb3Twofish.java | 11 +- .../keepass/tests/database/TestData.java | 11 +- .../tests/output/PwManagerOutputTest.java | 28 +---- .../keepass/database/element/Database.java | 100 +++++++++--------- .../database/element/PwDatabaseV3Debug.java | 45 -------- .../keepass/database/load/Importer.java | 21 ++-- .../database/load/ImporterFactory.java | 48 --------- .../keepass/database/load/ImporterV3.java | 74 ++----------- .../database/load/ImporterV3Debug.java | 43 -------- .../keepass/database/load/ImporterV4.java | 39 ++----- .../database/save/PwDbV3OutputDebug.java | 56 ---------- .../database/search/SearchDbHelper.java | 6 +- 13 files changed, 93 insertions(+), 402 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3Debug.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java index 25c574ccb..fc897cf0a 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java @@ -19,22 +19,12 @@ */ package com.kunzisoft.keepass.tests.database; -import android.content.Context; -import android.content.res.AssetManager; -import android.net.Uri; -import android.os.Environment; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.load.ImporterV3; -import com.kunzisoft.keepass.tests.TestUtil; -import com.kunzisoft.keepass.utils.UriUtil; - -import java.io.InputStream; -import java.io.File; - public class Kdb3 extends AndroidTestCase { private void testKeyfile(String dbAsset, String keyAsset, String password) throws Exception { + /* Context ctx = getContext(); File sdcard = Environment.getExternalStorageDirectory(); @@ -49,6 +39,7 @@ public class Kdb3 extends AndroidTestCase { importer.openDatabase(is, password, TestUtil.getKeyFileInputStream(ctx, keyPath)); is.close(); + */ } public void testXMLKeyFile() throws Exception { diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java index d39f37a29..3f41d341f 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java @@ -19,18 +19,11 @@ */ package com.kunzisoft.keepass.tests.database; -import java.io.InputStream; - -import android.content.Context; -import android.content.res.AssetManager; import android.test.AndroidTestCase; -import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; -import com.kunzisoft.keepass.database.load.ImporterV3; - public class Kdb3Twofish extends AndroidTestCase { public void testReadTwofish() throws Exception { + /* Context ctx = getContext(); AssetManager am = ctx.getAssets(); @@ -43,6 +36,6 @@ public class Kdb3Twofish extends AndroidTestCase { assertTrue(db.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish); is.close(); - + */ } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java index 7f4d059e4..98cc21f2d 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java @@ -19,16 +19,7 @@ */ package com.kunzisoft.keepass.tests.database; -import android.content.Context; -import android.content.res.AssetManager; -import android.net.Uri; - import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; -import com.kunzisoft.keepass.database.load.Importer; -import com.kunzisoft.keepass.tests.TestUtil; - -import java.io.InputStream; public class TestData { private static final String TEST1_KEYFILE = ""; @@ -37,6 +28,7 @@ public class TestData { private static Database mDb1; + /* public static Database GetDb1(Context ctx) throws Exception { return GetDb1(ctx, false); @@ -75,4 +67,5 @@ public class TestData { //return (PwDatabaseV3Debug) mDb1.getPwDatabase(); return null; } + */ } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java index 6841ccd1c..aa77c7843 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.java @@ -19,33 +19,12 @@ */ package com.kunzisoft.keepass.tests.output; -import static org.junit.Assert.assertArrayEquals; - -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.DigestOutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import android.content.res.AssetManager; import android.test.AndroidTestCase; - -import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; -import com.kunzisoft.keepass.database.element.PwDbHeader; -import com.kunzisoft.keepass.database.element.PwDbHeaderV3; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; -import com.kunzisoft.keepass.database.save.PwDbHeaderOutputV3; -import com.kunzisoft.keepass.database.save.PwDbV3Output; -import com.kunzisoft.keepass.database.save.PwDbV3OutputDebug; -import com.kunzisoft.keepass.stream.NullOutputStream; -import com.kunzisoft.keepass.tests.TestUtil; -import com.kunzisoft.keepass.tests.database.TestData; public class PwManagerOutputTest extends AndroidTestCase { - PwDatabaseV3Debug mPM; - + // PwDatabaseV3Debug mPM; + + /* @Override protected void setUp() throws Exception { super.setUp(); @@ -143,4 +122,5 @@ public class PwManagerOutputTest extends AndroidTestCase { assertArrayEquals("Databases do not match.", bExpected.toByteArray(), bActual.toByteArray()); } + */ } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 6cce4bcfe..085463a81 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -31,15 +31,13 @@ import com.kunzisoft.keepass.database.EntryHandler; import com.kunzisoft.keepass.database.GroupHandler; import com.kunzisoft.keepass.database.cursor.EntryCursorV3; import com.kunzisoft.keepass.database.cursor.EntryCursorV4; -import com.kunzisoft.keepass.database.exception.ContentFileNotFoundException; -import com.kunzisoft.keepass.database.exception.InvalidDBException; -import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; -import com.kunzisoft.keepass.database.load.Importer; -import com.kunzisoft.keepass.database.load.ImporterFactory; +import com.kunzisoft.keepass.database.exception.*; +import com.kunzisoft.keepass.database.load.ImporterV3; +import com.kunzisoft.keepass.database.load.ImporterV4; import com.kunzisoft.keepass.database.save.PwDbOutput; import com.kunzisoft.keepass.database.search.SearchDbHelper; import com.kunzisoft.keepass.icons.IconDrawableFactory; +import com.kunzisoft.keepass.stream.LEDataInputStream; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.UriUtil; @@ -56,9 +54,9 @@ public class Database { private static final String TAG = Database.class.getName(); private PwDatabase pwDatabase = null; - private PwDatabaseV3 pwDatabaseV3 = null; - private PwDatabaseV4 pwDatabaseV4 = null; private PwVersion version = null; + // To keep a reference for specific methods provided by V4 + private PwDatabaseV4 pwDatabaseV4 = null; private Uri mUri = null; private SearchDbHelper searchHelper = null; @@ -74,32 +72,27 @@ public class Database { public Database(String databasePath) { // TODO Test with kdb extension if (isKDBExtension(databasePath)) { - this.pwDatabaseV3 = new PwDatabaseV3(); - - this.pwDatabase = pwDatabaseV3; + setDatabaseV3(new PwDatabaseV3()); } else { PwGroupV4 groupV4 = new PwGroupV4(); - this.pwDatabaseV4 = new PwDatabaseV4(); + setDatabaseV4(new PwDatabaseV4()); groupV4.setTitle(dbNameFromPath(databasePath)); groupV4.setIconStandard(pwDatabaseV4.getIconFactory().getFolderIcon()); this.pwDatabaseV4.setRootGroup(groupV4); - - this.pwDatabase = pwDatabaseV4; } - this.version = pwDatabase.getVersion(); } - private void retrieveDatabaseVersioned(PwDatabase pwDatabase) { + private void setDatabaseV3(PwDatabaseV3 pwDatabaseV3) { + this.pwDatabaseV4 = null; + this.pwDatabase = pwDatabaseV3; + this.version = pwDatabase.getVersion(); + } + + private void setDatabaseV4(PwDatabaseV4 pwDatabaseV4) { + this.pwDatabaseV4 = pwDatabaseV4; + this.pwDatabase = pwDatabaseV4; this.version = pwDatabase.getVersion(); - switch (version) { - case V3: - pwDatabaseV3 = (PwDatabaseV3) pwDatabase; - break; - case V4: - pwDatabaseV4 = (PwDatabaseV4) pwDatabase; - break; - } } private boolean isKDBExtension(String filename) { @@ -145,11 +138,9 @@ public class Database { return pwDatabase.getIconFactory(); } - public void loadData(Context ctx, Uri uri, String password, Uri keyfile, ProgressTaskUpdater status) throws IOException, FileNotFoundException, InvalidDBException { - loadData(ctx, uri, password, keyfile, status, !Importer.DEBUG); - } + public void loadData(Context ctx, Uri uri, String password, Uri keyfile, ProgressTaskUpdater progressTaskUpdater) + throws IOException, InvalidDBException { - private void loadData(Context ctx, Uri uri, String password, Uri keyfile, ProgressTaskUpdater status, boolean debug) throws IOException, FileNotFoundException, InvalidDBException { mUri = uri; readOnly = false; if (uri.getScheme().equals("file")) { @@ -157,48 +148,60 @@ public class Database { readOnly = !file.canWrite(); } - passUrisAsInputStreams(ctx, uri, password, keyfile, status, debug); - } + // Pass Uris as InputStreams - private void passUrisAsInputStreams(Context ctx, Uri uri, String password, Uri keyfile, ProgressTaskUpdater status, boolean debug) throws IOException, FileNotFoundException, InvalidDBException { - InputStream is, kfIs; + InputStream inputStream, keyFileInputStream; try { - is = UriUtil.getUriInputStream(ctx, uri); + inputStream = UriUtil.getUriInputStream(ctx, uri); } catch (Exception e) { Log.e("KPD", "Database::loadData", e); throw ContentFileNotFoundException.getInstance(uri); } try { - kfIs = UriUtil.getUriInputStream(ctx, keyfile); + keyFileInputStream = UriUtil.getUriInputStream(ctx, keyfile); } catch (Exception e) { Log.e("KPD", "Database::loadData", e); throw ContentFileNotFoundException.getInstance(keyfile); } - loadData(ctx, is, password, kfIs, status, debug); - } - public void loadData(Context ctx, InputStream is, String password, InputStream keyFileInputStream, boolean debug) throws IOException, InvalidDBException { - loadData(ctx, is, password, keyFileInputStream, null, debug); - } + // Load Data - private void loadData(Context ctx, InputStream is, String password, InputStream keyFileInputStream, ProgressTaskUpdater progressTaskUpdater, boolean debug) throws IOException, InvalidDBException { - BufferedInputStream bis = new BufferedInputStream(is); - - if ( ! bis.markSupported() ) { + BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); + if ( ! bufferedInputStream.markSupported() ) { throw new IOException("Input stream does not support mark."); } // We'll end up reading 8 bytes to identify the header. Might as well use two extra. - bis.mark(10); + bufferedInputStream.mark(10); // Get the file directory to save the attachments - Importer databaseImporter = ImporterFactory.createImporter(bis, ctx.getFilesDir(), debug); + int sig1 = LEDataInputStream.readInt(bufferedInputStream); + int sig2 = LEDataInputStream.readInt(bufferedInputStream); - bis.reset(); // Return to the start + // Header of database V3 + if ( PwDbHeaderV3.matchesHeader(sig1, sig2) ) { + bufferedInputStream.reset(); // Return to the start + setDatabaseV3(new ImporterV3().openDatabase(bufferedInputStream, + password, + keyFileInputStream, + progressTaskUpdater)); + } + + // Header of database V4 + else if ( PwDbHeaderV4.matchesHeader(sig1, sig2) ) { + bufferedInputStream.reset(); // Return to the start + setDatabaseV4(new ImporterV4(ctx.getFilesDir()).openDatabase(bufferedInputStream, + password, + keyFileInputStream, + progressTaskUpdater)); + } + + // Header not recognized + else { + throw new InvalidDBSignatureException(); + } - pwDatabase = databaseImporter.openDatabase(bis, password, keyFileInputStream, progressTaskUpdater); - retrieveDatabaseVersioned(pwDatabase); if ( pwDatabase != null ) { try { passwordEncodingError = !pwDatabase.validatePasswordEncoding(password); @@ -344,7 +347,6 @@ public class Database { } pwDatabase = null; - pwDatabaseV3 = null; pwDatabaseV4 = null; mUri = null; loaded = false; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3Debug.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3Debug.java deleted file mode 100644 index f9fb0bdf0..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3Debug.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -public class PwDatabaseV3Debug extends PwDatabaseV3 { - - private byte[] postHeader; - private PwDbHeaderV3 dbHeader; - - @Override - public void copyEncrypted(byte[] buf, int offset, int size) { - postHeader = new byte[size]; - System.arraycopy(buf, offset, postHeader, 0, size); - } - - @Override - public void copyHeader(PwDbHeaderV3 header) { - dbHeader = header; - } - - public byte[] getPostHeader() { - return postHeader; - } - - public PwDbHeaderV3 getDbHeader() { - return dbHeader; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java index 1fc08ad3c..1b60c2e88 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -26,14 +26,19 @@ import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; import java.io.IOException; import java.io.InputStream; -public abstract class Importer { +public abstract class Importer { - public static final boolean DEBUG = true; - - public abstract PwDatabase openDatabase(InputStream inStream, String password, InputStream keyInputStream) - throws IOException, InvalidDBException; - - public abstract PwDatabase openDatabase(InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater updater) + /** + * Load a versioned database file, return contents in a new PwDatabase. + * + * @param inStream Existing file to load. + * @param password Pass phrase for infile. + * @return new PwDatabase container. + * + * @throws IOException on any file error. + * @throws InvalidDBException on database error. + */ + public abstract PwDb openDatabase(InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater updater) throws IOException, InvalidDBException; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java deleted file mode 100644 index 6fdd902c8..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.load; - -import com.kunzisoft.keepass.database.element.PwDbHeaderV3; -import com.kunzisoft.keepass.database.element.PwDbHeaderV4; -import com.kunzisoft.keepass.database.exception.InvalidDBSignatureException; -import com.kunzisoft.keepass.stream.LEDataInputStream; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -public class ImporterFactory { - - public static Importer createImporter(InputStream is, File streamDir, boolean debug) throws InvalidDBSignatureException, IOException { - int sig1 = LEDataInputStream.readInt(is); - int sig2 = LEDataInputStream.readInt(is); - - if ( PwDbHeaderV3.matchesHeader(sig1, sig2) ) { - if (debug) { - return new ImporterV3Debug(); - } - return new ImporterV3(); - } else if ( PwDbHeaderV4.matchesHeader(sig1, sig2) ) { - return new ImporterV4(streamDir); - } - - throw new InvalidDBSignatureException(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index 5f4fd6bb9..e7e04bb59 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -46,94 +46,36 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA package com.kunzisoft.keepass.database.load; import android.util.Log; - import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwDate; -import com.kunzisoft.keepass.database.element.PwDbHeader; -import com.kunzisoft.keepass.database.element.PwDbHeaderV3; -import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwGroupV3; -import com.kunzisoft.keepass.database.element.PwNodeIdUUID; -import com.kunzisoft.keepass.database.exception.InvalidAlgorithmException; -import com.kunzisoft.keepass.database.exception.InvalidDBException; -import com.kunzisoft.keepass.database.exception.InvalidDBSignatureException; -import com.kunzisoft.keepass.database.exception.InvalidDBVersionException; -import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; -import com.kunzisoft.keepass.database.exception.InvalidPasswordException; +import com.kunzisoft.keepass.database.element.*; +import com.kunzisoft.keepass.database.exception.*; import com.kunzisoft.keepass.stream.LEDataInputStream; import com.kunzisoft.keepass.stream.NullOutputStream; import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; import com.kunzisoft.keepass.utils.Types; +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -import java.security.DigestOutputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.security.*; import java.util.Arrays; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - /** * Load a v3 database file. * * @author Naomaru Itoi * @author Bill Zwicky */ -public class ImporterV3 extends Importer { +public class ImporterV3 extends Importer { private static final String TAG = ImporterV3.class.getName(); private PwDatabaseV3 databaseToOpen; - public ImporterV3() { - super(); - } - - protected PwDatabaseV3 createDB() { - return new PwDatabaseV3(); - } - - /** - * Load a v3 database file, return contents in a new PwDatabaseV3. - * - * @param inStream Existing file to load. - * @param password Pass phrase for infile. - * @return new PwDatabaseV3 container. - * - * @throws IOException on any file error. - * @throws InvalidKeyFileException - * @throws InvalidPasswordException - * @throws InvalidPasswordException on a decryption error, or possible internal bug. - * @throws InvalidDBSignatureException - * @throws InvalidDBVersionException - * @throws IllegalBlockSizeException on a decryption error, or possible internal bug. - * @throws BadPaddingException on a decryption error, or possible internal bug. - * @throws NoSuchAlgorithmException on a decryption error, or possible internal bug. - * @throws NoSuchPaddingException on a decryption error, or possible internal bug. - * @throws InvalidAlgorithmParameterException if error decrypting main file body. - * @throws ShortBufferException if error decrypting main file body. - */ - @Override - public PwDatabaseV3 openDatabase( InputStream inStream, String password, InputStream kfIs) - throws IOException, InvalidDBException { - return openDatabase(inStream, password, kfIs, null); - } - @Override public PwDatabaseV3 openDatabase(InputStream inStream, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater) throws IOException, InvalidDBException { @@ -160,7 +102,7 @@ public class ImporterV3 extends Importer { if (progressTaskUpdater != null) progressTaskUpdater.updateMessage(R.string.retrieving_db_key); - databaseToOpen = createDB(); + databaseToOpen = new PwDatabaseV3(); databaseToOpen.retrieveMasterKey(password, kfIs); // Select algorithm diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java deleted file mode 100644 index 069c463f2..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3Debug.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.load; - -import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; -import com.kunzisoft.keepass.database.exception.InvalidDBException; -import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; - -import java.io.IOException; -import java.io.InputStream; - -public class ImporterV3Debug extends ImporterV3 { - - @Override - protected PwDatabaseV3Debug createDB() { - return new PwDatabaseV3Debug(); - } - - @Override - public PwDatabaseV3Debug openDatabase(InputStream inStream, String password, - InputStream keyInputStream, ProgressTaskUpdater status) throws IOException, - InvalidDBException { - return (PwDatabaseV3Debug) super.openDatabase(inStream, password, keyInputStream, status); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index be0690d67..c80724dd3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -19,22 +19,14 @@ */ package com.kunzisoft.keepass.database.load; +import biz.source_code.base64Coder.Base64Coder; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.crypto.CipherFactory; import com.kunzisoft.keepass.crypto.PwStreamCipherFactory; import com.kunzisoft.keepass.crypto.engine.CipherEngine; import com.kunzisoft.keepass.database.ITimeLogger; import com.kunzisoft.keepass.database.PwCompressionAlgorithm; -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwDatabaseV4XML; -import com.kunzisoft.keepass.database.element.PwDate; -import com.kunzisoft.keepass.database.element.PwDbHeaderV4; -import com.kunzisoft.keepass.database.element.PwDeletedObject; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupV4; -import com.kunzisoft.keepass.database.element.PwIconCustom; -import com.kunzisoft.keepass.database.element.PwNodeIdUUID; +import com.kunzisoft.keepass.database.element.*; import com.kunzisoft.keepass.database.exception.ArcFourException; import com.kunzisoft.keepass.database.exception.InvalidDBException; import com.kunzisoft.keepass.database.exception.InvalidPasswordException; @@ -49,17 +41,14 @@ import com.kunzisoft.keepass.utils.DateUtil; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MemUtil; import com.kunzisoft.keepass.utils.Types; - import org.spongycastle.crypto.StreamCipher; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import java.io.*; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -70,12 +59,7 @@ import java.util.Stack; import java.util.UUID; import java.util.zip.GZIPInputStream; -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; - -import biz.source_code.base64Coder.Base64Coder; - -public class ImporterV4 extends Importer { +public class ImporterV4 extends Importer { private StreamCipher randomStream; private PwDatabaseV4 mDatabase; @@ -87,18 +71,11 @@ public class ImporterV4 extends Importer { public ImporterV4(File streamDir) { this.streamDir = streamDir; } - - @Override - public PwDatabaseV4 openDatabase(InputStream inStream, String password, - InputStream keyInputStream) throws IOException, InvalidDBException { - - return openDatabase(inStream, password, keyInputStream, null); - } @Override public PwDatabaseV4 openDatabase(InputStream inStream, String password, - InputStream keyInputStream, ProgressTaskUpdater progressTaskUpdater) throws IOException, - InvalidDBException { + InputStream keyInputStream, ProgressTaskUpdater progressTaskUpdater) + throws IOException, InvalidDBException { if (progressTaskUpdater != null) progressTaskUpdater.updateMessage(R.string.retrieving_db_key); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java deleted file mode 100644 index a419f0dfb..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3OutputDebug.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwDatabaseV3Debug; -import com.kunzisoft.keepass.database.element.PwDbHeaderV3; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; - -import java.io.OutputStream; -import java.security.SecureRandom; - -public class PwDbV3OutputDebug extends PwDbV3Output { - PwDatabaseV3Debug debugDb; - private boolean noHeaderHash; - - public PwDbV3OutputDebug(PwDatabaseV3 pm, OutputStream os, boolean noHeaderHash) { - super(pm, os); - debugDb = (PwDatabaseV3Debug) pm; - this.noHeaderHash = noHeaderHash; - } - - @Override - protected SecureRandom setIVs(PwDbHeaderV3 header) throws PwDbOutputException { - // Reuse random values to test equivalence in debug mode - PwDbHeaderV3 origHeader = debugDb.getDbHeader(); - System.arraycopy(origHeader.encryptionIV, 0, header.encryptionIV, 0, origHeader.encryptionIV.length); - System.arraycopy(origHeader.masterSeed, 0, header.masterSeed, 0, origHeader.masterSeed.length); - System.arraycopy(origHeader.transformSeed, 0, header.transformSeed, 0, origHeader.transformSeed.length); - - return null; - } - - @Override - protected boolean useHeaderHash() { - return !noHeaderHash; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java index aafdda43f..49bf7605d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java @@ -86,9 +86,9 @@ public class SearchDbHelper { private boolean entryContainsString(PwEntryInterface entry, String qStr, Locale loc) { // Search all strings in the entry - Iterator iter = EntrySearchStringIterator.getInstance(entry); - while (iter.hasNext()) { - String str = iter.next(); + Iterator iterator = EntrySearchStringIterator.getInstance(entry); + while (iterator.hasNext()) { + String str = iterator.next(); if (str != null && str.length() != 0) { String lower = str.toLowerCase(loc); if (lower.contains(qStr)) { From eabd2d3e4c9e13ffa1e33922eae9aa44caf99e7f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 3 May 2019 17:15:53 +0200 Subject: [PATCH 100/289] Refactor --- .../keepass/tests/database/DeleteEntry.java | 22 +- .../keepass/tests/search/SearchTest.java | 12 +- .../keepass/activities/EntryActivity.java | 9 +- .../keepass/activities/EntryEditActivity.java | 22 +- .../keepass/activities/GroupActivity.java | 81 ++--- .../keepass/activities/ListNodesFragment.java | 18 +- .../keepass/adapters/NodeAdapter.java | 65 ++-- .../adapters/SearchEntryCursorAdapter.java | 8 +- .../keepass/autofill/AutofillHelper.kt | 29 +- .../keepass/database/BinaryPool.java | 18 +- .../{GroupHandler.java => NodeHandler.java} | 2 +- .../keepass/database/SortNodeEnum.java | 74 ++-- .../database/action/node/AddEntryRunnable.kt | 8 +- .../database/action/node/AddGroupRunnable.kt | 6 +- .../node/AfterActionNodeFinishRunnable.kt | 4 +- .../database/action/node/CopyEntryRunnable.kt | 10 +- .../action/node/DeleteEntryRunnable.kt | 8 +- .../action/node/DeleteGroupRunnable.java | 10 +- .../database/action/node/MoveEntryRunnable.kt | 10 +- .../database/action/node/MoveGroupRunnable.kt | 8 +- .../action/node/UpdateEntryRunnable.kt | 12 +- .../action/node/UpdateGroupRunnable.kt | 12 +- .../keepass/database/cursor/EntryCursor.java | 7 +- .../keepass/database/element/Database.java | 325 ++++++++--------- .../database/element/EntryVersioned.kt | 341 ++++++++++++++++++ .../database/element/GroupVersioned.kt | 299 +++++++++++++++ .../keepass/database/element/NodeVersioned.kt | 12 + .../keepass/database/element/PwDatabase.java | 59 +-- .../database/element/PwDatabaseV3.java | 94 ++++- .../database/element/PwDatabaseV4.java | 106 +++--- .../database/element/PwDbHeaderV4.java | 18 +- .../keepass/database/element/PwEntry.kt | 17 + .../database/element/PwEntryInterface.java | 113 ------ .../database/element/PwEntryInterface.kt | 14 + .../keepass/database/element/PwEntryV3.java | 61 +--- .../keepass/database/element/PwEntryV4.java | 31 +- .../keepass/database/element/PwGroup.java | 98 ----- .../keepass/database/element/PwGroup.kt | 67 ++++ .../database/element/PwGroupInterface.java | 57 --- .../database/element/PwGroupInterface.kt | 38 ++ .../keepass/database/element/PwGroupV3.java | 27 +- .../keepass/database/element/PwGroupV4.java | 31 +- .../keepass/database/element/PwNode.java | 72 ++-- .../database/element/PwNodeInterface.java | 56 --- .../database/element/PwNodeInterface.kt | 32 ++ .../iterator/EntrySearchStringIterator.java | 65 ---- .../EntrySearchStringIterator.kt} | 31 +- .../iterator/EntrySearchStringIteratorV4.java | 97 ----- .../iterator/EntrySearchStringIteratorV4.kt | 87 +++++ .../keepass/database/load/ImporterV3.java | 42 +-- .../keepass/database/load/ImporterV4.java | 15 +- .../keepass/database/save/PwDbV3Output.java | 45 +-- .../keepass/database/save/PwDbV4Output.java | 53 +-- .../database/save/PwEntryOutputV3.java | 3 +- .../database/search/EntrySearchHandlerV4.java | 23 +- .../database/search/SearchDbHelper.java | 101 ------ .../keepass/database/search/SearchDbHelper.kt | 88 +++++ .../dialogs/GroupEditDialogFragment.java | 4 +- .../kunzisoft/keepass/utils/SprContextV4.java | 3 +- .../kunzisoft/keepass/utils/SprEngineV4.java | 37 +- 60 files changed, 1706 insertions(+), 1411 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/database/{GroupHandler.java => NodeHandler.java} (95%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/EntryVersioned.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/GroupVersioned.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/NodeVersioned.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java rename app/src/main/java/com/kunzisoft/keepass/database/{EntryHandler.java => iterator/EntrySearchStringIterator.kt} (52%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index 227c5ca94..15a46383c 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -19,19 +19,11 @@ */ package com.kunzisoft.keepass.tests.database; -import android.content.Context; import android.test.AndroidTestCase; - -import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; -import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.GroupVersioned; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.search.SearchDbHelper; - -import java.util.List; public class DeleteEntry extends AndroidTestCase { private static final String GROUP1_NAME = "Group1"; @@ -57,7 +49,7 @@ public class DeleteEntry extends AndroidTestCase { } PwDatabaseV3 pm = (PwDatabaseV3) db.getPwDatabase(); - PwGroupInterface group1 = getGroup(pm, GROUP1_NAME); + GroupVersioned group1 = getGroup(pm, GROUP1_NAME); assertNotNull("Could not find group1", group1); // Delete the group @@ -73,8 +65,8 @@ public class DeleteEntry extends AndroidTestCase { // Verify the entries were removed from the search index SearchDbHelper dbHelp = new SearchDbHelper(ctx); - PwGroupInterface results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME, 100); - PwGroupInterface results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME, 100); + GroupVersioned results1 = dbHelp.search(db.getPwDatabase(), ENTRY1_NAME, 100); + GroupVersioned results2 = dbHelp.search(db.getPwDatabase(), ENTRY2_NAME, 100); assertEquals("Entry1 was not removed from the search results", 0, results1.numbersOfChildEntries()); assertEquals("Entry2 was not removed from the search results", 0, results2.numbersOfChildEntries()); @@ -101,11 +93,11 @@ public class DeleteEntry extends AndroidTestCase { } - private PwGroupInterface getGroup(PwDatabase pm, String name) { + private GroupVersioned getGroup(PwDatabase pm, String name) { /* - List groups = pm.getGroups(); + List groups = pm.getGroups(); for ( int i = 0; i < groups.size(); i++ ) { - PwGroupInterface group = groups.get(i); + GroupVersioned group = groups.get(i); if ( group.getTitle().equals(name) ) { return group; } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java index 6163c3fff..55dccb177 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java @@ -24,10 +24,8 @@ import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.test.AndroidTestCase; - import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.tests.database.TestData; +import com.kunzisoft.keepass.database.element.GroupVersioned; public class SearchTest extends AndroidTestCase { @@ -37,25 +35,25 @@ public class SearchTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); - mDb = TestData.GetDb1(getContext(), true); + //mDb = TestData.GetDb1(getContext(), true); } public void testSearch() { - PwGroupInterface results = mDb.search("Amazon"); + GroupVersioned results = mDb.search("Amazon"); //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); } public void testBackupIncluded() { updateOmitSetting(false); - PwGroupInterface results = mDb.search("BackupOnly"); + GroupVersioned results = mDb.search("BackupOnly"); //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); } public void testBackupExcluded() { updateOmitSetting(true); - PwGroupInterface results = mDb.search("BackupOnly"); + GroupVersioned results = mDb.search("BackupOnly"); //assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0); } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 1dc39b442..5b9070006 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -37,7 +37,6 @@ import android.view.MenuItem; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; - import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; @@ -45,7 +44,7 @@ import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.database.ExtraFields; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.EntryVersioned; import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.notifications.NotificationCopyingService; @@ -75,7 +74,7 @@ public class EntryActivity extends LockingHideActivity { private EntryContentsView entryContentsView; private Toolbar toolbar; - protected PwEntryInterface mEntry; + protected EntryVersioned mEntry; private boolean mShowPassword; private ClipboardHelper clipboardHelper; @@ -83,7 +82,7 @@ public class EntryActivity extends LockingHideActivity { private int iconColor; - public static void launch(Activity activity, PwEntryInterface pw, boolean readOnly) { + public static void launch(Activity activity, EntryVersioned pw, boolean readOnly) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryActivity.class); intent.putExtra(KEY_ENTRY, pw.getNodeId()); @@ -318,7 +317,7 @@ public class EntryActivity extends LockingHideActivity { database.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); // Assign title text - titleView.setText(PwEntryInterface.getVisualTitle(mEntry)); + titleView.setText(mEntry.getVisualTitle()); // Assign basic fields entryContentsView.assignUserName(mEntry.getUsername()); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 47303e9f6..58fbf3d60 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -46,13 +46,7 @@ import com.kunzisoft.keepass.database.action.node.ActionNodeValues; import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwDate; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwIconStandard; -import com.kunzisoft.keepass.database.element.PwNodeId; +import com.kunzisoft.keepass.database.element.*; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; @@ -85,9 +79,9 @@ public class EntryEditActivity extends LockingHideActivity private Database database; - protected PwEntryInterface mEntry; - protected PwGroupInterface mParent; - protected PwEntryInterface mCallbackNewEntry; + protected EntryVersioned mEntry; + protected GroupVersioned mParent; + protected EntryVersioned mCallbackNewEntry; protected boolean mIsNew; protected PwIconStandard mSelectedIconStandard; @@ -112,7 +106,7 @@ public class EntryEditActivity extends LockingHideActivity * @param activity from activity * @param pwEntry Entry to update */ - public static void launch(Activity activity, PwEntryInterface pwEntry) { + public static void launch(Activity activity, EntryVersioned pwEntry) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryEditActivity.class); intent.putExtra(KEY_ENTRY, pwEntry.getNodeId()); @@ -126,7 +120,7 @@ public class EntryEditActivity extends LockingHideActivity * @param activity from activity * @param pwGroup Group who will contains new entry */ - public static void launch(Activity activity, PwGroupInterface pwGroup) { + public static void launch(Activity activity, GroupVersioned pwGroup) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, EntryEditActivity.class); intent.putExtra(KEY_PARENT, pwGroup.getNodeId()); @@ -415,10 +409,10 @@ public class EntryEditActivity extends LockingHideActivity return errorValidation.isValidate; } - protected PwEntryInterface populateNewEntry() { + protected EntryVersioned populateNewEntry() { Database database = App.getDB(); - PwEntryInterface newEntry = mEntry.duplicate(); + EntryVersioned newEntry = new EntryVersioned(mEntry); database.startManageEntry(newEntry); database.createBackupOf(newEntry); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 8249b4d5f..fba39248e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -71,12 +71,7 @@ import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable; import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable; import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwNodeId; -import com.kunzisoft.keepass.database.element.PwNodeInterface; +import com.kunzisoft.keepass.database.element.*; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; @@ -129,17 +124,17 @@ public class GroupActivity extends LockingActivity private ListNodesFragment listNodesFragment; private boolean currentGroupIsASearch; - private PwGroupInterface rootGroup; - private PwGroupInterface mCurrentGroup; - private PwGroupInterface oldGroupToUpdate; - private PwNodeInterface nodeToCopy; - private PwNodeInterface nodeToMove; + private GroupVersioned rootGroup; + private GroupVersioned mCurrentGroup; + private GroupVersioned oldGroupToUpdate; + private NodeVersioned nodeToCopy; + private NodeVersioned nodeToMove; private SearchEntryCursorAdapter searchSuggestionAdapter; private int iconColor; - private static void buildAndLaunchIntent(Activity activity, PwGroupInterface group, boolean readOnly, + private static void buildAndLaunchIntent(Activity activity, GroupVersioned group, boolean readOnly, IntentBuildLauncher intentBuildLauncher) { if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { Intent intent = new Intent(activity, GroupActivity.class); @@ -165,7 +160,7 @@ public class GroupActivity extends LockingActivity launch(activity, null, readOnly); } - public static void launch(Activity activity, PwGroupInterface group, boolean readOnly) { + public static void launch(Activity activity, GroupVersioned group, boolean readOnly) { TimeoutHelper.INSTANCE.recordTime(activity); buildAndLaunchIntent(activity, group, readOnly, (intent) -> activity.startActivityForResult(intent, 0)); @@ -316,7 +311,7 @@ public class GroupActivity extends LockingActivity } } - private void openSearchGroup(PwGroupInterface group) { + private void openSearchGroup(GroupVersioned group) { // Delete the previous search fragment Fragment searchFragment = getSupportFragmentManager().findFragmentByTag(SEARCH_FRAGMENT_TAG); if (searchFragment != null) { @@ -328,11 +323,11 @@ public class GroupActivity extends LockingActivity openGroup(group, true); } - private void openChildGroup(PwGroupInterface group) { + private void openChildGroup(GroupVersioned group) { openGroup(group, false); } - private void openGroup(PwGroupInterface group, boolean isASearch) { + private void openGroup(GroupVersioned group, boolean isASearch) { // Check TimeoutHelper TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeoutOrResetTimeout(this, () -> { // Open a group in a new fragment @@ -375,7 +370,7 @@ public class GroupActivity extends LockingActivity super.onSaveInstanceState(outState); } - protected @Nullable PwGroupInterface retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { + protected @Nullable GroupVersioned retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { // If it's a search if ( Intent.ACTION_SEARCH.equals(intent.getAction()) ) { @@ -395,7 +390,7 @@ public class GroupActivity extends LockingActivity setReadOnly(database.isReadOnly() || getReadOnly()); // Force read only if the database is like that Log.w(TAG, "Creating tree view"); - PwGroupInterface currentGroup; + GroupVersioned currentGroup; if (pwGroupId == null) { currentGroup = rootGroup; } else { @@ -486,18 +481,18 @@ public class GroupActivity extends LockingActivity } @Override - public void onNodeClick(PwNodeInterface node) { + public void onNodeClick(NodeVersioned node) { switch (node.getType()) { case GROUP: try { - openChildGroup((PwGroupInterface) node); + openChildGroup((GroupVersioned) node); } catch (ClassCastException e) { Log.e(TAG, "Node can't be cast in Group"); } break; case ENTRY: try { - PwEntryInterface entry = ((PwEntryInterface) node); + EntryVersioned entry = ((EntryVersioned) node); EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), () -> { EntryActivity.launch(GroupActivity.this, entry, getReadOnly()); @@ -531,7 +526,7 @@ public class GroupActivity extends LockingActivity } } - private Entry getEntry(PwEntryInterface entry) { + private Entry getEntry(EntryVersioned entry) { Entry entryModel = new Entry(); entryModel.setTitle(entry.getTitle()); entryModel.setUsername(entry.getUsername()); @@ -547,29 +542,29 @@ public class GroupActivity extends LockingActivity } @Override - public boolean onOpenMenuClick(PwNodeInterface node) { + public boolean onOpenMenuClick(NodeVersioned node) { onNodeClick(node); return true; } @Override - public boolean onEditMenuClick(PwNodeInterface node) { + public boolean onEditMenuClick(NodeVersioned node) { switch (node.getType()) { case GROUP: - oldGroupToUpdate = (PwGroupInterface) node; + oldGroupToUpdate = (GroupVersioned) node; GroupEditDialogFragment.build(oldGroupToUpdate) .show(getSupportFragmentManager(), GroupEditDialogFragment.TAG_CREATE_GROUP); break; case ENTRY: - EntryEditActivity.launch(GroupActivity.this, (PwEntryInterface) node); + EntryEditActivity.launch(GroupActivity.this, (EntryVersioned) node); break; } return true; } @Override - public boolean onCopyMenuClick(PwNodeInterface node) { + public boolean onCopyMenuClick(NodeVersioned node) { toolbarPasteExpandableLayout.expand(); nodeToCopy = node; @@ -590,7 +585,7 @@ public class GroupActivity extends LockingActivity Log.e(TAG, "Copy not allowed for group"); break; case ENTRY: - copyEntry((PwEntryInterface) nodeToCopy, mCurrentGroup); + copyEntry((EntryVersioned) nodeToCopy, mCurrentGroup); break; } nodeToCopy = null; @@ -600,7 +595,7 @@ public class GroupActivity extends LockingActivity } } - private void copyEntry(PwEntryInterface entryToCopy, PwGroupInterface newParent) { + private void copyEntry(EntryVersioned entryToCopy, GroupVersioned newParent) { new Thread(new CopyEntryRunnable(this, App.getDB(), entryToCopy, @@ -611,7 +606,7 @@ public class GroupActivity extends LockingActivity } @Override - public boolean onMoveMenuClick(PwNodeInterface node) { + public boolean onMoveMenuClick(NodeVersioned node) { toolbarPasteExpandableLayout.expand(); nodeToMove = node; @@ -629,10 +624,10 @@ public class GroupActivity extends LockingActivity case R.id.menu_paste: switch (nodeToMove.getType()) { case GROUP: - moveGroup((PwGroupInterface) nodeToMove, mCurrentGroup); + moveGroup((GroupVersioned) nodeToMove, mCurrentGroup); break; case ENTRY: - moveEntry((PwEntryInterface) nodeToMove, mCurrentGroup); + moveEntry((EntryVersioned) nodeToMove, mCurrentGroup); break; } nodeToMove = null; @@ -642,7 +637,7 @@ public class GroupActivity extends LockingActivity } } - private void moveGroup(PwGroupInterface groupToMove, PwGroupInterface newParent) { + private void moveGroup(GroupVersioned groupToMove, GroupVersioned newParent) { new Thread(new MoveGroupRunnable( this, App.getDB(), @@ -653,7 +648,7 @@ public class GroupActivity extends LockingActivity ).start(); } - private void moveEntry(PwEntryInterface entryToMove, PwGroupInterface newParent) { + private void moveEntry(EntryVersioned entryToMove, GroupVersioned newParent) { new Thread(new MoveEntryRunnable( this, App.getDB(), @@ -665,19 +660,19 @@ public class GroupActivity extends LockingActivity } @Override - public boolean onDeleteMenuClick(PwNodeInterface node) { + public boolean onDeleteMenuClick(NodeVersioned node) { switch (node.getType()) { case GROUP: - deleteGroup((PwGroupInterface) node); + deleteGroup((GroupVersioned) node); break; case ENTRY: - deleteEntry((PwEntryInterface) node); + deleteEntry((EntryVersioned) node); break; } return true; } - private void deleteGroup(PwGroupInterface group) { + private void deleteGroup(GroupVersioned group) { //TODO Verify trash recycle bin new Thread(new DeleteGroupRunnable( this, @@ -688,7 +683,7 @@ public class GroupActivity extends LockingActivity ).start(); } - private void deleteEntry(PwEntryInterface entry) { + private void deleteEntry(EntryVersioned entry) { new Thread(new DeleteEntryRunnable( this, App.getDB(), @@ -978,7 +973,7 @@ public class GroupActivity extends LockingActivity case CREATION: // If group creation // Build the group - PwGroupInterface newGroup = database.createGroup(); + GroupVersioned newGroup = database.createGroup(); newGroup.setTitle(name); newGroup.setIcon(icon); // Not really needed here because added in runnable but safe @@ -997,7 +992,7 @@ public class GroupActivity extends LockingActivity case UPDATE: // If update add new elements if (oldGroupToUpdate != null) { - PwGroupInterface updateGroup = oldGroupToUpdate.duplicate(); + GroupVersioned updateGroup = new GroupVersioned(oldGroupToUpdate); updateGroup.setTitle(name); // TODO custom icon updateGroup.setIcon(icon); @@ -1056,11 +1051,11 @@ public class GroupActivity extends LockingActivity listNodesFragment.removeNode(actionNodeValues.getOldNode()); if (actionNodeValues.getOldNode() != null) { - PwGroupInterface parent = actionNodeValues.getOldNode().getParent(); + GroupVersioned parent = actionNodeValues.getOldNode().getParent(); Database database = App.getDB(); if (database.isRecycleBinAvailable() && database.isRecycleBinEnabled()) { - PwGroupInterface recycleBin = database.getRecycleBin(); + GroupVersioned recycleBin = database.getRecycleBin(); // Add trash if it doesn't exists if (parent.equals(recycleBin) && mCurrentGroup != null diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java index 100365e95..9d82dcc76 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java @@ -20,8 +20,8 @@ import android.view.ViewGroup; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.adapters.NodeAdapter; import com.kunzisoft.keepass.database.SortNodeEnum; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwNodeInterface; +import com.kunzisoft.keepass.database.element.GroupVersioned; +import com.kunzisoft.keepass.database.element.NodeVersioned; import com.kunzisoft.keepass.dialogs.SortDialogFragment; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishFragment; @@ -39,7 +39,7 @@ public class ListNodesFragment extends StylishFragment implements private OnScrollListener onScrollListener; private RecyclerView listView; - private PwGroupInterface currentGroup; + private GroupVersioned currentGroup; private NodeAdapter mAdapter; private View notFoundView; @@ -50,7 +50,7 @@ public class ListNodesFragment extends StylishFragment implements private boolean readOnly; - public static ListNodesFragment newInstance(PwGroupInterface group, boolean readOnly, boolean isASearch) { + public static ListNodesFragment newInstance(GroupVersioned group, boolean readOnly, boolean isASearch) { Bundle bundle = new Bundle(); if (group != null) { bundle.putParcelable(GROUP_KEY, group); @@ -245,7 +245,7 @@ public class ListNodesFragment extends StylishFragment implements case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE: if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE || resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { - PwNodeInterface newNode = data.getParcelableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY); + NodeVersioned newNode = data.getParcelableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY); if (newNode != null) { if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE) mAdapter.addNode(newNode); @@ -265,19 +265,19 @@ public class ListNodesFragment extends StylishFragment implements return mAdapter == null || mAdapter.getItemCount() <= 0; } - public void addNode(PwNodeInterface newNode) { + public void addNode(NodeVersioned newNode) { mAdapter.addNode(newNode); } - public void updateNode(PwNodeInterface oldNode, PwNodeInterface newNode) { + public void updateNode(NodeVersioned oldNode, NodeVersioned newNode) { mAdapter.updateNode(oldNode, newNode); } - public void removeNode(PwNodeInterface pwNode) { + public void removeNode(NodeVersioned pwNode) { mAdapter.removeNode(pwNode); } - public PwGroupInterface getMainGroup() { + public GroupVersioned getMainGroup() { return currentGroup; } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 304189db7..3dcdd0a43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -27,28 +27,19 @@ import android.support.v7.util.SortedList; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.util.SortedListAdapterCallback; import android.util.Log; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; 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.Database; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwNodeInterface; +import com.kunzisoft.keepass.database.element.*; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.utils.Util; public class NodeAdapter extends RecyclerView.Adapter { private static final String TAG = NodeAdapter.class.getName(); - private SortedList nodeSortedList; + private SortedList nodeSortedList; private Context context; private LayoutInflater inflater; @@ -85,17 +76,17 @@ public class NodeAdapter extends RecyclerView.Adapter { this.readOnly = false; this.isASearchResult = false; - this.nodeSortedList = new SortedList<>(PwNodeInterface.class, new SortedListAdapterCallback(this) { - @Override public int compare(PwNodeInterface item1, PwNodeInterface item2) { + this.nodeSortedList = new SortedList<>(NodeVersioned.class, new SortedListAdapterCallback(this) { + @Override public int compare(NodeVersioned item1, NodeVersioned item2) { return listSort.getNodeComparator(ascendingSort, groupsBeforeSort).compare(item1, item2); } - @Override public boolean areContentsTheSame(PwNodeInterface oldItem, PwNodeInterface newItem) { + @Override public boolean areContentsTheSame(NodeVersioned oldItem, NodeVersioned newItem) { return oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getIcon().equals(newItem.getIcon()); } - @Override public boolean areItemsTheSame(PwNodeInterface item1, PwNodeInterface item2) { + @Override public boolean areItemsTheSame(NodeVersioned item1, NodeVersioned item2) { return item1.equals(item2); } }); @@ -143,12 +134,12 @@ public class NodeAdapter extends RecyclerView.Adapter { /** * Rebuild the list by clear and build children from the group */ - public void rebuildList(PwGroupInterface group) { + public void rebuildList(GroupVersioned group) { this.nodeSortedList.clear(); assignPreferences(); // TODO verify sort try { - this.nodeSortedList.addAll(group.getChildrenWithoutMetastream()); + this.nodeSortedList.addAll(group.getChildEntriesWithoutMetaStream()); } catch (Exception e) { Log.e(TAG, "Can't add node elements to the list", e); Toast.makeText(context, "Can't add node elements to the list : " + e.getMessage(), Toast.LENGTH_LONG).show(); @@ -167,7 +158,7 @@ public class NodeAdapter extends RecyclerView.Adapter { * Add a node to the list * @param node Node to add */ - public void addNode(PwNodeInterface node) { + public void addNode(NodeVersioned node) { nodeSortedList.add(node); } @@ -175,7 +166,7 @@ public class NodeAdapter extends RecyclerView.Adapter { * Remove a node in the list * @param node Node to delete */ - public void removeNode(PwNodeInterface node) { + public void removeNode(NodeVersioned node) { nodeSortedList.remove(node); } @@ -184,7 +175,7 @@ public class NodeAdapter extends RecyclerView.Adapter { * @param oldNode Node before the update * @param newNode Node after the update */ - public void updateNode(PwNodeInterface oldNode, PwNodeInterface newNode) { + public void updateNode(NodeVersioned oldNode, NodeVersioned newNode) { nodeSortedList.beginBatchedUpdates(); nodeSortedList.remove(oldNode); nodeSortedList.add(newNode); @@ -210,7 +201,7 @@ public class NodeAdapter extends RecyclerView.Adapter { public BasicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { BasicViewHolder basicViewHolder; View view; - if (viewType == PwNodeInterface.Type.GROUP.ordinal()) { + if (viewType == Type.GROUP.ordinal()) { view = inflater.inflate(R.layout.list_nodes_group, parent, false); basicViewHolder = new GroupViewHolder(view); } else { @@ -222,7 +213,7 @@ public class NodeAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(@NonNull BasicViewHolder holder, int position) { - PwNodeInterface subNode = nodeSortedList.get(position); + NodeVersioned subNode = nodeSortedList.get(position); // Assign image int iconColor = Color.BLACK; switch (subNode.getType()) { @@ -248,12 +239,12 @@ public class NodeAdapter extends RecyclerView.Adapter { // Add username holder.subText.setText(""); holder.subText.setVisibility(View.GONE); - if (subNode.getType().equals(PwNodeInterface.Type.ENTRY)) { - PwEntryInterface entry = (PwEntryInterface) subNode; + if (subNode.getType().equals(Type.ENTRY)) { + EntryVersioned entry = (EntryVersioned) subNode; database.startManageEntry(entry); - holder.text.setText(PwEntryInterface.getVisualTitle(entry)); + holder.text.setText(entry.getVisualTitle()); String username = entry.getUsername(); if (showUsernames && !username.isEmpty()) { @@ -295,27 +286,27 @@ public class NodeAdapter extends RecyclerView.Adapter { * Callback listener to redefine to do an action when a node is click */ public interface NodeClickCallback { - void onNodeClick(PwNodeInterface node); + void onNodeClick(NodeVersioned node); } /** * Menu listener to redefine to do an action in menu */ public interface NodeMenuListener { - boolean onOpenMenuClick(PwNodeInterface node); - boolean onEditMenuClick(PwNodeInterface node); - boolean onCopyMenuClick(PwNodeInterface node); - boolean onMoveMenuClick(PwNodeInterface node); - boolean onDeleteMenuClick(PwNodeInterface node); + boolean onOpenMenuClick(NodeVersioned node); + boolean onEditMenuClick(NodeVersioned node); + boolean onCopyMenuClick(NodeVersioned node); + boolean onMoveMenuClick(NodeVersioned node); + boolean onDeleteMenuClick(NodeVersioned node); } /** * Utility class for node listener */ private class OnNodeClickListener implements View.OnClickListener { - private PwNodeInterface node; + private NodeVersioned node; - OnNodeClickListener(PwNodeInterface node) { + OnNodeClickListener(NodeVersioned node) { this.node = node; } @@ -331,11 +322,11 @@ public class NodeAdapter extends RecyclerView.Adapter { */ private class ContextMenuBuilder implements View.OnCreateContextMenuListener { - private PwNodeInterface node; + private NodeVersioned node; private NodeMenuListener menuListener; private boolean readOnly; - ContextMenuBuilder(PwNodeInterface node, NodeMenuListener menuListener, boolean readOnly) { + ContextMenuBuilder(NodeVersioned node, NodeMenuListener menuListener, boolean readOnly) { this.menuListener = menuListener; this.node = node; this.readOnly = readOnly; @@ -363,7 +354,7 @@ public class NodeAdapter extends RecyclerView.Adapter { if (readOnly || isASearchResult || node.equals(database.getRecycleBin()) - || node.getType().equals(PwNodeInterface.Type.GROUP)) { + || node.getType().equals(Type.GROUP)) { // TODO COPY For Group contextMenu.removeItem(R.id.menu_copy); } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java index ca81259e3..7e11dd460 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java @@ -33,7 +33,7 @@ import android.widget.TextView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.database.cursor.EntryCursor; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.element.EntryVersioned; import com.kunzisoft.keepass.database.element.PwIcon; import com.kunzisoft.keepass.database.element.PwIconFactory; import com.kunzisoft.keepass.settings.PreferencesUtil; @@ -104,7 +104,7 @@ public class SearchEntryCursorAdapter extends CursorAdapter { database.getDrawFactory().assignDatabaseIconTo(context, viewHolder.imageViewIcon, icon, iconColor); // Assign title - String showTitle = PwEntryInterface.getVisualTitle(false, title, username, url, uuid.toString()); + String showTitle = EntryVersioned.CREATOR.getVisualTitle(false, title, username, url, uuid.toString()); viewHolder.textViewTitle.setText(showTitle); if (displayUsername && !username.isEmpty()) { viewHolder.textViewSubTitle.setText(String.format("(%s)", username)); @@ -124,8 +124,8 @@ public class SearchEntryCursorAdapter extends CursorAdapter { return database.searchEntry(constraint.toString()); } - public PwEntryInterface getEntryFromPosition(int position) { - PwEntryInterface pwEntry = null; + public EntryVersioned getEntryFromPosition(int position) { + EntryVersioned pwEntry = null; Cursor cursor = this.getCursor(); if (cursor.moveToFirst() diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt index 26c610e88..7eaf623d4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -33,7 +33,7 @@ import android.view.autofill.AutofillValue import android.widget.RemoteViews import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.EntrySelectionHelper -import com.kunzisoft.keepass.database.element.PwEntryInterface +import com.kunzisoft.keepass.database.element.EntryVersioned import java.util.* @@ -42,7 +42,7 @@ object AutofillHelper { private const val AUTOFILL_RESPONSE_REQUEST_CODE = 8165 - private const val ASSIST_STRUCTURE = android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE + private const val ASSIST_STRUCTURE = AutofillManager.EXTRA_ASSIST_STRUCTURE fun retrieveAssistStructure(intent: Intent?): AssistStructure? { intent?.let { @@ -51,7 +51,7 @@ object AutofillHelper { return null } - private fun makeEntryTitle(entry: PwEntryInterface): String { + private fun makeEntryTitle(entry: EntryVersioned): String { if (!entry.title.isEmpty() && !entry.username.isEmpty()) return String.format("%s (%s)", entry.title, entry.username) if (!entry.title.isEmpty()) @@ -62,24 +62,21 @@ object AutofillHelper { // TODO No title } - private fun buildDataset(context: Context, entry: PwEntryInterface, + private fun buildDataset(context: Context, + entry: EntryVersioned, struct: StructureParser.Result): Dataset? { val title = makeEntryTitle(entry) val views = newRemoteViews(context.packageName, title) val builder = Dataset.Builder(views) builder.setId(entry.nodeId.toString()) - if (entry.password != null) { - val value = AutofillValue.forText(entry.password) - struct.password.forEach { id -> builder.setValue(id, value) } - } - if (entry.username != null) { - val value = AutofillValue.forText(entry.username) - val ids = ArrayList(struct.username) - if (entry.username.contains("@") || struct.username.isEmpty()) - ids.addAll(struct.email) - ids.forEach { id -> builder.setValue(id, value) } - } + struct.password.forEach { id -> builder.setValue(id, AutofillValue.forText(entry.password)) } + + val ids = ArrayList(struct.username) + if (entry.username.contains("@") || struct.username.isEmpty()) + ids.addAll(struct.email) + ids.forEach { id -> builder.setValue(id, AutofillValue.forText(entry.username)) } + return try { builder.build() } catch (e: IllegalArgumentException) { @@ -91,7 +88,7 @@ object AutofillHelper { /** * Method to hit when right key is selected */ - fun buildResponseWhenEntrySelected(activity: Activity, entry: PwEntryInterface) { + fun buildResponseWhenEntrySelected(activity: Activity, entry: EntryVersioned) { val mReplyIntent: Intent activity.intent?.let { intent -> if (intent.extras.containsKey(ASSIST_STRUCTURE)) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java index edc1864f0..14e5f8df9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java @@ -19,9 +19,7 @@ */ package com.kunzisoft.keepass.database; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.GroupVersioned; import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.security.ProtectedBinary; @@ -38,7 +36,7 @@ public class BinaryPool { } public BinaryPool(PwGroupV4 rootGroup) { - build(rootGroup); + // TODO ? build(rootGroup); } public ProtectedBinary get(int key) { @@ -92,12 +90,13 @@ public class BinaryPool { return -1; } - - private void build(PwGroupV4 rootGroup) { - PwGroupInterface.doForEachChild(rootGroup, new EntryHandler() { + + private void build(GroupVersioned rootGroup) { + /* + rootGroup.doForEachChild(new EntryHandler() { @Override - public boolean operate(PwEntryInterface entryInterface) { - PwEntryV4 entry = (PwEntryV4) entryInterface; + public boolean operate(EntryVersioned entryInterface) { + PwEntryV4 entry = entryInterface.getPwEntryV4(); for (PwEntryV4 histEntry : entry.getHistory()) { add(histEntry.getBinaries()); @@ -106,5 +105,6 @@ public class BinaryPool { return true; } }, null); + */ } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/GroupHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/GroupHandler.java rename to app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.java index 8e83b4f1c..55aed8d20 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/GroupHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.java @@ -25,6 +25,6 @@ package com.kunzisoft.keepass.database; * @author bpellin * */ -public abstract class GroupHandler { +public abstract class NodeHandler { public abstract boolean operate(T group); } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java index b59df0f16..d9d616bf0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java @@ -20,16 +20,16 @@ package com.kunzisoft.keepass.database; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwNodeInterface; +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; public enum SortNodeEnum { DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME; - public Comparator getNodeComparator(boolean ascending, boolean groupsBefore) { + public Comparator getNodeComparator(boolean ascending, boolean groupsBefore) { switch (this) { case DB: return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort @@ -47,7 +47,7 @@ public enum SortNodeEnum { } } - private static abstract class NodeComparator implements Comparator { + private static abstract class NodeComparator implements Comparator { boolean ascending; boolean groupsBefore; @@ -56,19 +56,19 @@ public enum SortNodeEnum { this.groupsBefore = groupsBefore; } - int compareWith(Comparator comparatorGroup, - Comparator comparatorEntry, - PwNodeInterface object1, - PwNodeInterface object2, + int compareWith(Comparator comparatorGroup, + Comparator comparatorEntry, + NodeVersioned object1, + NodeVersioned object2, int resultOfNodeMethodCompare) { if (object1.equals(object2)) return 0; - if (object1 instanceof PwGroupInterface) { - if (object2 instanceof PwGroupInterface) { + if (object1 instanceof GroupVersioned) { + if (object2 instanceof GroupVersioned) { return comparatorGroup - .compare((PwGroupInterface) object1, (PwGroupInterface) object2); - } else if (object2 instanceof PwEntryInterface) { + .compare((GroupVersioned) object1, (GroupVersioned) object2); + } else if (object2 instanceof EntryVersioned) { if(groupsBefore) return -1; else @@ -76,11 +76,11 @@ public enum SortNodeEnum { } else { return -1; } - } else if (object1 instanceof PwEntryInterface) { - if(object2 instanceof PwEntryInterface) { + } else if (object1 instanceof EntryVersioned) { + if(object2 instanceof EntryVersioned) { return comparatorEntry - .compare((PwEntryInterface) object1, (PwEntryInterface) object2); - } else if (object2 instanceof PwGroupInterface) { + .compare((EntryVersioned) object1, (EntryVersioned) object2); + } else if (object2 instanceof GroupVersioned) { if(groupsBefore) return 1; else @@ -106,7 +106,7 @@ public enum SortNodeEnum { super(ascending, groupsBefore); } - public int compare(PwNodeInterface object1, PwNodeInterface object2) { + public int compare(NodeVersioned object1, NodeVersioned object2) { return compareWith( new GroupNameComparator(ascending), @@ -129,7 +129,7 @@ public enum SortNodeEnum { } @Override - public int compare(PwNodeInterface object1, PwNodeInterface object2) { + public int compare(NodeVersioned object1, NodeVersioned object2) { return compareWith( new GroupCreationComparator(ascending), @@ -151,7 +151,7 @@ public enum SortNodeEnum { } @Override - public int compare(PwNodeInterface object1, PwNodeInterface object2) { + public int compare(NodeVersioned object1, NodeVersioned object2) { return compareWith( new GroupLastModificationComparator(ascending), @@ -173,7 +173,7 @@ public enum SortNodeEnum { } @Override - public int compare(PwNodeInterface object1, PwNodeInterface object2) { + public int compare(NodeVersioned object1, NodeVersioned object2) { return compareWith( new GroupLastAccessComparator(ascending), @@ -205,13 +205,13 @@ public enum SortNodeEnum { /** * Group comparator by name */ - public static class GroupNameComparator extends AscendingComparator { + public static class GroupNameComparator extends AscendingComparator { GroupNameComparator(boolean ascending) { super(ascending); } - public int compare(PwGroupInterface object1, PwGroupInterface object2) { + public int compare(GroupVersioned object1, GroupVersioned object2) { if (object1.equals(object2)) return 0; @@ -228,13 +228,13 @@ public enum SortNodeEnum { /** * Group comparator by name */ - public static class GroupCreationComparator extends AscendingComparator { + public static class GroupCreationComparator extends AscendingComparator { GroupCreationComparator(boolean ascending) { super(ascending); } - public int compare(PwGroupInterface object1, PwGroupInterface object2) { + public int compare(GroupVersioned object1, GroupVersioned object2) { if (object1.equals(object2)) return 0; @@ -252,13 +252,13 @@ public enum SortNodeEnum { /** * Group comparator by last modification */ - public static class GroupLastModificationComparator extends AscendingComparator { + public static class GroupLastModificationComparator extends AscendingComparator { GroupLastModificationComparator(boolean ascending) { super(ascending); } - public int compare(PwGroupInterface object1, PwGroupInterface object2) { + public int compare(GroupVersioned object1, GroupVersioned object2) { if (object1.equals(object2)) return 0; @@ -276,13 +276,13 @@ public enum SortNodeEnum { /** * Group comparator by last access */ - public static class GroupLastAccessComparator extends AscendingComparator { + public static class GroupLastAccessComparator extends AscendingComparator { GroupLastAccessComparator(boolean ascending) { super(ascending); } - public int compare(PwGroupInterface object1, PwGroupInterface object2) { + public int compare(GroupVersioned object1, GroupVersioned object2) { if (object1.equals(object2)) return 0; @@ -300,13 +300,13 @@ public enum SortNodeEnum { /** * Comparator of Entry by Name */ - public static class EntryNameComparator extends AscendingComparator { + public static class EntryNameComparator extends AscendingComparator { EntryNameComparator(boolean ascending) { super(ascending); } - public int compare(PwEntryInterface object1, PwEntryInterface object2) { + public int compare(EntryVersioned object1, EntryVersioned object2) { if (object1.equals(object2)) return 0; @@ -323,13 +323,13 @@ public enum SortNodeEnum { /** * Comparator of Entry by Creation */ - public static class EntryCreationComparator extends AscendingComparator { + public static class EntryCreationComparator extends AscendingComparator { EntryCreationComparator(boolean ascending) { super(ascending); } - public int compare(PwEntryInterface object1, PwEntryInterface object2) { + public int compare(EntryVersioned object1, EntryVersioned object2) { if (object1.equals(object2)) return 0; @@ -347,13 +347,13 @@ public enum SortNodeEnum { /** * Comparator of Entry by Last Modification */ - public static class EntryLastModificationComparator extends AscendingComparator { + public static class EntryLastModificationComparator extends AscendingComparator { EntryLastModificationComparator(boolean ascending) { super(ascending); } - public int compare(PwEntryInterface object1, PwEntryInterface object2) { + public int compare(EntryVersioned object1, EntryVersioned object2) { if (object1.equals(object2)) return 0; @@ -371,13 +371,13 @@ public enum SortNodeEnum { /** * Comparator of Entry by Last Access */ - public static class EntryLastAccessComparator extends AscendingComparator { + public static class EntryLastAccessComparator extends AscendingComparator { EntryLastAccessComparator(boolean ascending) { super(ascending); } - public int compare(PwEntryInterface object1, PwEntryInterface object2) { + public int compare(EntryVersioned object1, EntryVersioned object2) { if (object1.equals(object2)) return 0; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt index e770e13d6..8a245e027 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt @@ -21,14 +21,14 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntryInterface -import com.kunzisoft.keepass.database.element.PwGroupInterface +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.GroupVersioned class AddEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mNewEntry: PwEntryInterface, - private val mParent: PwGroupInterface, + private val mNewEntry: EntryVersioned, + private val mParent: GroupVersioned, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt index 5daf7eb05..9a4c15fe6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt @@ -21,13 +21,13 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwGroupInterface +import com.kunzisoft.keepass.database.element.GroupVersioned class AddGroupRunnable constructor( context: FragmentActivity, database: Database, - private val mNewGroup: PwGroupInterface, - private val mParent: PwGroupInterface, + private val mNewGroup: GroupVersioned, + private val mParent: GroupVersioned, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt index 1ff76ece1..18eb29dee 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AfterActionNodeFinishRunnable.kt @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.action.node -import com.kunzisoft.keepass.database.element.PwNodeInterface +import com.kunzisoft.keepass.database.element.NodeVersioned /** * Callback method who return the node(s) modified after an action @@ -29,7 +29,7 @@ import com.kunzisoft.keepass.database.element.PwNodeInterface * - Move : @param oldNode NULL, @param NodeToMove * - Update : @param oldNode NodeToUpdate, @param NodeUpdated */ -data class ActionNodeValues(val success: Boolean, val message: String?, val oldNode: PwNodeInterface?, val newNode: PwNodeInterface?) +data class ActionNodeValues(val success: Boolean, val message: String?, val oldNode: NodeVersioned?, val newNode: NodeVersioned?) abstract class AfterActionNodeFinishRunnable { abstract fun onActionNodeFinish(actionNodeValues: ActionNodeValues) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt index c04c79a8a..07c98413b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt @@ -22,19 +22,19 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import android.util.Log import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntryInterface -import com.kunzisoft.keepass.database.element.PwGroupInterface +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.GroupVersioned class CopyEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mEntryToCopy: PwEntryInterface, - private val mNewParent: PwGroupInterface, + private val mEntryToCopy: EntryVersioned, + private val mNewParent: GroupVersioned, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { - private var mEntryCopied: PwEntryInterface? = null + private var mEntryCopied: EntryVersioned? = null override fun nodeAction() { // Update entry with new values diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt index 0a8b4c2b4..93c58eafb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt @@ -21,18 +21,18 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntryInterface -import com.kunzisoft.keepass.database.element.PwGroupInterface +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.GroupVersioned class DeleteEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mEntryToDelete: PwEntryInterface, + private val mEntryToDelete: EntryVersioned, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { - private var mParent: PwGroupInterface? = null + private var mParent: GroupVersioned? = null private var mRecycle: Boolean = false diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java index 251dc5245..e1b9e4aab 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.action.node; import android.support.v4.app.FragmentActivity; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.GroupVersioned; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,13 +30,13 @@ import org.jetbrains.annotations.Nullable; // TODO Kotlinized public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { - private PwGroupInterface mGroupToDelete; - private PwGroupInterface mParent; + private GroupVersioned mGroupToDelete; + private GroupVersioned mParent; private boolean mRecycle; public DeleteGroupRunnable(FragmentActivity context, Database database, - PwGroupInterface group, + GroupVersioned group, AfterActionNodeFinishRunnable finish, boolean save) { super(context, database, finish, save); @@ -67,7 +67,7 @@ public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { } else { // Let's not bother recovering from a failure to save a deleted tree. It is too much work. - // TODO TEST pm.undoDeleteGroup(mGroup, mParent); + // TODO TEST pm.undoDeleteGroupFrom(mGroup, mParent); } } return new ActionNodeValues(isSuccess, message, mGroupToDelete, null); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt index f76732a7b..64ddcad1a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt @@ -22,19 +22,19 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import android.util.Log import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntryInterface -import com.kunzisoft.keepass.database.element.PwGroupInterface +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.GroupVersioned class MoveEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mEntryToMove: PwEntryInterface?, - private val mNewParent: PwGroupInterface, + private val mEntryToMove: EntryVersioned?, + private val mNewParent: GroupVersioned, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { - private var mOldParent: PwGroupInterface? = null + private var mOldParent: GroupVersioned? = null override fun nodeAction() { // Move entry in new parent diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt index f5f0aa5b7..ac1cf96b2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt @@ -23,18 +23,18 @@ import android.support.v4.app.FragmentActivity import android.util.Log import com.kunzisoft.keepass.R import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwGroupInterface +import com.kunzisoft.keepass.database.element.GroupVersioned class MoveGroupRunnable constructor( context: FragmentActivity, database: Database, - private val mGroupToMove: PwGroupInterface?, - private val mNewParent: PwGroupInterface, + private val mGroupToMove: GroupVersioned?, + private val mNewParent: GroupVersioned, afterAddNodeRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, afterAddNodeRunnable, save) { - private var mOldParent: PwGroupInterface? = null + private var mOldParent: GroupVersioned? = null override fun nodeAction() { mGroupToMove?.let { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt index 399e185d8..944b1bbff 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt @@ -21,30 +21,30 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwEntryInterface +import com.kunzisoft.keepass.database.element.EntryVersioned class UpdateEntryRunnable constructor( context: FragmentActivity, database: Database, - private val mOldEntry: PwEntryInterface, - private val mNewEntry: PwEntryInterface, + private val mOldEntry: EntryVersioned, + private val mNewEntry: EntryVersioned, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { // Keep backup of original values in case save fails - private val mBackupEntry: PwEntryInterface = mOldEntry.duplicate() + private val mBackupEntry: EntryVersioned = EntryVersioned(mOldEntry) override fun nodeAction() { // Update entry with new values mOldEntry.touch(true, true) - database.updateEntry(mOldEntry, mNewEntry) + mOldEntry.updateWith(mNewEntry) } override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { if (!isSuccess) { // If we fail to save, back out changes to global structure - database.updateEntry(mOldEntry, mBackupEntry) + mOldEntry.updateWith(mBackupEntry) } return ActionNodeValues(isSuccess, message, mOldEntry, mNewEntry) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt index 4d97a6846..9059b1df4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt @@ -21,30 +21,30 @@ package com.kunzisoft.keepass.database.action.node import android.support.v4.app.FragmentActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.PwGroupInterface +import com.kunzisoft.keepass.database.element.GroupVersioned class UpdateGroupRunnable constructor( context: FragmentActivity, database: Database, - private val mOldGroup: PwGroupInterface, - private val mNewGroup: PwGroupInterface, + private val mOldGroup: GroupVersioned, + private val mNewGroup: GroupVersioned, finishRunnable: AfterActionNodeFinishRunnable?, save: Boolean) : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { // Keep backup of original values in case save fails - private val mBackupGroup: PwGroupInterface = mOldGroup.duplicate() + private val mBackupGroup: GroupVersioned = GroupVersioned(mOldGroup) override fun nodeAction() { // Update group with new values mOldGroup.touch(true, true) - database.updateGroup(mOldGroup, mNewGroup) + mOldGroup.updateWith(mNewGroup) } override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { if (!isSuccess) { // If we fail to save, back out changes to global structure - database.updateGroup(mOldGroup, mBackupGroup) + mOldGroup.updateWith(mBackupGroup) } return ActionNodeValues(isSuccess, message, mOldGroup, mNewGroup) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java index ce613685a..ddc70d1d4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java @@ -3,14 +3,11 @@ package com.kunzisoft.keepass.database.cursor; import android.database.MatrixCursor; import android.provider.BaseColumns; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwIconFactory; -import com.kunzisoft.keepass.database.element.PwIconStandard; -import com.kunzisoft.keepass.database.element.PwNodeIdUUID; +import com.kunzisoft.keepass.database.element.*; import java.util.UUID; -public abstract class EntryCursor extends MatrixCursor { +public abstract class EntryCursor extends MatrixCursor { protected long entryId; public static final String _ID = BaseColumns._ID; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java index 085463a81..0511866b8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java @@ -27,8 +27,7 @@ import android.util.Log; import android.webkit.URLUtil; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; +import com.kunzisoft.keepass.database.NodeHandler; import com.kunzisoft.keepass.database.cursor.EntryCursorV3; import com.kunzisoft.keepass.database.cursor.EntryCursorV4; import com.kunzisoft.keepass.database.exception.*; @@ -55,7 +54,8 @@ public class Database { private PwDatabase pwDatabase = null; private PwVersion version = null; - // To keep a reference for specific methods provided by V4 + // To keep a reference for specific methods provided by version + private PwDatabaseV3 pwDatabaseV3 = null; private PwDatabaseV4 pwDatabaseV4 = null; private Uri mUri = null; @@ -84,12 +84,14 @@ public class Database { } private void setDatabaseV3(PwDatabaseV3 pwDatabaseV3) { + this.pwDatabaseV3 = pwDatabaseV3; this.pwDatabaseV4 = null; this.pwDatabase = pwDatabaseV3; this.version = pwDatabase.getVersion(); } private void setDatabaseV4(PwDatabaseV4 pwDatabaseV4) { + this.pwDatabaseV3 = null; this.pwDatabaseV4 = pwDatabaseV4; this.pwDatabase = pwDatabaseV4; this.version = pwDatabase.getVersion(); @@ -179,9 +181,9 @@ public class Database { int sig1 = LEDataInputStream.readInt(bufferedInputStream); int sig2 = LEDataInputStream.readInt(bufferedInputStream); + bufferedInputStream.reset(); // Return to the start // Header of database V3 if ( PwDbHeaderV3.matchesHeader(sig1, sig2) ) { - bufferedInputStream.reset(); // Return to the start setDatabaseV3(new ImporterV3().openDatabase(bufferedInputStream, password, keyFileInputStream, @@ -190,7 +192,6 @@ public class Database { // Header of database V4 else if ( PwDbHeaderV4.matchesHeader(sig1, sig2) ) { - bufferedInputStream.reset(); // Return to the start setDatabaseV4(new ImporterV4(ctx.getFilesDir()).openDatabase(bufferedInputStream, password, keyFileInputStream, @@ -214,14 +215,24 @@ public class Database { } } - public PwGroupInterface search(String str) { + public Boolean isGroupSearchable(GroupVersioned group, Boolean isOmitBackup) { + switch (version) { + case V3: + return pwDatabaseV3.isGroupSearchable(group.getPwGroupV3(), isOmitBackup); + case V4: + return pwDatabaseV4.isGroupSearchable(group.getPwGroupV4(), isOmitBackup); + } + return false; + } + + public GroupVersioned search(String str) { return search(str, Integer.MAX_VALUE); } - public PwGroupInterface search(String str, int max) { + public GroupVersioned search(String str, int max) { if (searchHelper == null) return null; - return searchHelper.search(pwDatabase, str, max); + return searchHelper.search(this, str, max); } public Cursor searchEntry(String query) { @@ -229,11 +240,11 @@ public class Database { case V3: EntryCursorV3 cursorV3 = new EntryCursorV3(); if (!query.isEmpty()) { - PwGroupInterface searchResult = search(query, 6); + GroupVersioned searchResult = search(query, 6); if (searchResult != null) { - for (PwEntryInterface entry: searchResult.getChildEntries()) { + for (EntryVersioned entry: searchResult.getChildEntries()) { if (!entry.isMetaStream()) { // TODO metastream - cursorV3.addEntry((PwEntryV3) entry); + cursorV3.addEntry(entry.getPwEntryV3()); } } } @@ -242,11 +253,11 @@ public class Database { case V4: EntryCursorV4 cursorv4 = new EntryCursorV4(); if (!query.isEmpty()) { - PwGroupInterface searchResult = search(query, 6); + GroupVersioned searchResult = search(query, 6); if (searchResult != null) { - for (PwEntryInterface entry: searchResult.getChildEntries()) { + for (EntryVersioned entry: searchResult.getChildEntries()) { if (!entry.isMetaStream()) { // TODO metastream - cursorv4.addEntry((PwEntryV4) entry); + cursorv4.addEntry(entry.getPwEntryV4()); } } } @@ -256,25 +267,25 @@ public class Database { return null; } - public PwEntryInterface getEntryFrom(Cursor cursor) { + public EntryVersioned getEntryFrom(Cursor cursor) { PwIconFactory iconFactory = pwDatabase.getIconFactory(); - PwEntryInterface pwEntry = createEntry(); + EntryVersioned entry = createEntry(); try { switch (version) { case V3: - ((EntryCursorV3) cursor).populateEntry((PwEntryV3) pwEntry, iconFactory); + ((EntryCursorV3) cursor).populateEntry(entry.getPwEntryV3(), iconFactory); break; case V4: // TODO invert field reference manager - startManageEntry(pwEntry); - ((EntryCursorV4) cursor).populateEntry((PwEntryV4) pwEntry, iconFactory); - stopManageEntry(pwEntry); + startManageEntry(entry); + ((EntryCursorV4) cursor).populateEntry(entry.getPwEntryV4(), iconFactory); + stopManageEntry(entry); break; } } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be populated", e); } - return pwEntry; + return entry; } public void saveData(Context ctx) throws IOException, PwDbOutputException { @@ -567,107 +578,113 @@ public class Database { pwDatabase.retrieveMasterKey(key, keyInputStream); } - public PwGroupInterface getRootGroup() { - return pwDatabase.getRootGroup(); + public GroupVersioned getRootGroup() { + switch (version) { + case V3: + return new GroupVersioned(pwDatabaseV3.getRootGroup()); + case V4: + return new GroupVersioned(pwDatabaseV4.getRootGroup()); + } + return null; } - public PwEntryInterface createEntry() { + public EntryVersioned createEntry() { + EntryVersioned newEntry = null; try { switch (version) { case V3: - return new PwEntryV3(); + newEntry = new EntryVersioned(new PwEntryV3()); + newEntry.setNodeId(pwDatabaseV3.newEntryId()); + case V4: - return new PwEntryV4(); + newEntry = new EntryVersioned(new PwEntryV4()); + newEntry.setNodeId(pwDatabaseV4.newEntryId()); } } catch (Exception e) { Log.e(TAG, "This version of PwEntry can't be created", e); } - return null; + return newEntry; } - public PwGroupInterface createGroup() { - PwGroupInterface newPwGroup = null; + public GroupVersioned createGroup() { + GroupVersioned newPwGroup = null; try { switch (version) { case V3: - newPwGroup = new PwGroupV3(); + newPwGroup = new GroupVersioned(new PwGroupV3()); + newPwGroup.setNodeId(pwDatabaseV3.newGroupId()); + case V4: - newPwGroup = new PwGroupV4(); + newPwGroup = new GroupVersioned(new PwGroupV4()); + newPwGroup.setNodeId(pwDatabaseV4.newGroupId()); } - newPwGroup.setNodeId(pwDatabase.newGroupId()); } catch (Exception e) { Log.e(TAG, "This version of PwGroup can't be created", e); } return newPwGroup; } - public PwEntryInterface getEntryById(PwNodeId id) { - return pwDatabase.getEntryById(id); + public EntryVersioned getEntryById(PwNodeId id) { + PwEntryV3 entryV3 = pwDatabaseV3.getEntryById(id); + if (entryV3 != null) + return new EntryVersioned(entryV3); + + PwEntryV4 entryV4 = pwDatabaseV4.getEntryById(id); + if (entryV4 != null) + return new EntryVersioned(entryV4); + + return null; } - public PwGroupInterface getGroupById(PwNodeId id) { - return pwDatabase.getGroupById(id); + public GroupVersioned getGroupById(PwNodeId id) { + if (pwDatabaseV3 != null) { + PwGroupV3 groupV3 = pwDatabaseV3.getGroupById(id); + if (groupV3 != null) + return new GroupVersioned(groupV3); + } + + if (pwDatabaseV4 != null) { + PwGroupV4 groupV4 = pwDatabaseV4.getGroupById(id); + if (groupV4 != null) + return new GroupVersioned(groupV4); + } + + return null; } - public void addEntryTo(PwEntryInterface entry, PwGroupInterface parent) { - try { - pwDatabase.addEntryTo(entry, parent); - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be added from this version of PwGroup", e); + public void addEntryTo(EntryVersioned entry, GroupVersioned parent) { + switch (version) { + case V3: + pwDatabaseV3.addEntryTo(entry.getPwEntryV3(), parent.getPwGroupV3()); + case V4: + pwDatabaseV4.addEntryTo(entry.getPwEntryV4(), parent.getPwGroupV4()); } } - public void removeEntryFrom(PwEntryInterface entry, PwGroupInterface parent) { - try { - pwDatabase.removeEntryFrom(entry, parent); - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be removed from this version of PwGroup", e); + public void removeEntryFrom(EntryVersioned entry, GroupVersioned parent) { + switch (version) { + case V3: + pwDatabaseV3.removeEntryFrom(entry.getPwEntryV3(), parent.getPwGroupV3()); + case V4: + pwDatabaseV4.removeEntryFrom(entry.getPwEntryV4(), parent.getPwGroupV4()); } } - public void addGroupTo(PwGroupInterface group, PwGroupInterface parent) { - try { - pwDatabase.addGroupTo(group, parent); - } catch (Exception e) { - Log.e(TAG, "This version of PwGroup can't be added in this version of PwGroup", e); + public void addGroupTo(GroupVersioned group, GroupVersioned parent) { + switch (version) { + case V3: + pwDatabaseV3.addGroupTo(group.getPwGroupV3(), parent.getPwGroupV3()); + case V4: + pwDatabaseV4.addGroupTo(group.getPwGroupV4(), parent.getPwGroupV4()); } } - public void removeGroupFrom(PwGroupInterface group, PwGroupInterface parent) { - try { - pwDatabase.removeGroupFrom(group, parent); - } catch (Exception e) { - Log.e(TAG, "This version of PwGroup can't be removed from this version of PwGroup", e); - } - } - - public void updateEntry(PwEntryInterface oldEntry, PwEntryInterface newEntry) { - try { - switch (version) { - case V3: - ((PwEntryV3) oldEntry).updateWith((PwEntryV3) newEntry); - break; - case V4: - ((PwEntryV4) oldEntry).updateWith((PwEntryV4) newEntry); - break; - } - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be updated", e); - } - } - - public void updateGroup(PwGroupInterface oldGroup, PwGroupInterface newGroup) { - try { - switch (version) { - case V3: - ((PwGroupV3) oldGroup).updateWith((PwGroupV3) newGroup); - break; - case V4: - ((PwGroupV4) oldGroup).updateWith((PwGroupV4) newGroup); - break; - } - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be updated", e); + public void removeGroupFrom(GroupVersioned group, GroupVersioned parent) { + switch (version) { + case V3: + pwDatabaseV3.removeGroupFrom(group.getPwGroupV3(), parent.getPwGroupV3()); + case V4: + pwDatabaseV4.removeGroupFrom(group.getPwGroupV4(), parent.getPwGroupV4()); } } @@ -676,15 +693,15 @@ public class Database { * @param entryToCopy * @param newParent */ - public @Nullable PwEntryInterface copyEntry(PwEntryInterface entryToCopy, PwGroupInterface newParent) { + public @Nullable EntryVersioned copyEntry(EntryVersioned entryToCopy, GroupVersioned newParent) { try { - PwEntryInterface entryCopied = null; + EntryVersioned entryCopied = null; switch (version) { case V3: - entryCopied = entryToCopy.duplicate(); + entryCopied = new EntryVersioned(entryToCopy); break; case V4: - entryCopied = entryToCopy.duplicate(); + entryCopied = new EntryVersioned(entryToCopy); break; } entryCopied.setNodeId(new PwNodeIdUUID()); @@ -697,45 +714,55 @@ public class Database { return null; } - public void moveEntry(PwEntryInterface entryToMove, PwGroupInterface newParent) { + public void moveEntry(EntryVersioned entryToMove, GroupVersioned newParent) { removeEntryFrom(entryToMove, entryToMove.getParent()); addEntryTo(entryToMove, newParent); } - public void moveGroup(PwGroupInterface groupToMove, PwGroupInterface newParent) { + public void moveGroup(GroupVersioned groupToMove, GroupVersioned newParent) { removeGroupFrom(groupToMove, groupToMove.getParent()); addGroupTo(groupToMove, newParent); } - public void deleteEntry(PwEntryInterface entry) { + public void deleteEntry(EntryVersioned entry) { removeEntryFrom(entry, entry.getParent()); } - public void deleteGroup(PwGroupInterface group) { - PwGroupInterface.doForEachChildAndForRoot(group, - new EntryHandler() { + public void deleteGroup(GroupVersioned group) { + group.doForEachChildAndForIt( + new NodeHandler() { @Override - public boolean operate(PwEntryInterface entry) { + public boolean operate(EntryVersioned entry) { deleteEntry(entry); return true; } }, - new GroupHandler() { + new NodeHandler() { @Override - public boolean operate(PwGroupInterface group) { - PwGroupInterface parent = group.getParent(); + public boolean operate(GroupVersioned group) { + GroupVersioned parent = group.getParent(); removeGroupFrom(group, parent); return true; } }); } - public void undoDeleteEntry(PwEntryInterface entry, PwGroupInterface parent) { - pwDatabase.undoDeleteEntryFrom(entry, parent); + public void undoDeleteEntry(EntryVersioned entry, GroupVersioned parent) { + switch (version) { + case V3: + pwDatabaseV3.undoDeleteEntryFrom(entry.getPwEntryV3(), parent.getPwGroupV3()); + case V4: + pwDatabaseV4.undoDeleteEntryFrom(entry.getPwEntryV4(), parent.getPwGroupV4()); + } } - public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface parent) { - pwDatabase.undoDeleteGroup(group, parent); + public void undoDeleteGroup(GroupVersioned group, GroupVersioned parent) { + switch (version) { + case V3: + pwDatabaseV3.undoDeleteGroupFrom(group.getPwGroupV3(), parent.getPwGroupV3()); + case V4: + pwDatabaseV4.undoDeleteGroupFrom(group.getPwGroupV4(), parent.getPwGroupV4()); + } } /** @@ -743,122 +770,76 @@ public class Database { * @return true if RecycleBin available */ public boolean isRecycleBinAvailable() { - if (pwDatabaseV4 != null) { - switch (version) { - case V4: - return true; - } - } - return false; + return pwDatabaseV4 != null; } public boolean isRecycleBinEnabled() { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - return pwDatabaseV4.isRecycleBinEnabled(); - } + return pwDatabaseV4.isRecycleBinEnabled(); } return false; } - public PwGroupInterface getRecycleBin() { + public GroupVersioned getRecycleBin() { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - return pwDatabaseV4.getRecycleBin(); - } + return new GroupVersioned(pwDatabaseV4.getRecycleBin()); } return null; } - public boolean canRecycle(PwEntryInterface entry) { + public boolean canRecycle(EntryVersioned entry) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - return pwDatabaseV4.canRecycle(entry); - } + return pwDatabaseV4.canRecycle(entry.getPwEntryV4()); } return false; } - public boolean canRecycle(PwGroupInterface group) { + public boolean canRecycle(GroupVersioned group) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - return pwDatabaseV4.canRecycle(group); - } + return pwDatabaseV4.canRecycle(group.getPwGroupV4()); } return false; } - public void recycle(PwEntryInterface entry) { + public void recycle(EntryVersioned entry) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - pwDatabaseV4.recycle(entry); - break; - } + pwDatabaseV4.recycle(entry.getPwEntryV4()); } } - public void recycle(PwGroupInterface group) { + public void recycle(GroupVersioned group) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - pwDatabaseV4.recycle(group); - break; - } + pwDatabaseV4.recycle(group.getPwGroupV4()); } } - public void undoRecycle(PwEntryInterface entry, PwGroupInterface parent) { + public void undoRecycle(EntryVersioned entry, GroupVersioned parent) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - pwDatabaseV4.undoRecycle(entry, parent); - break; - } + pwDatabaseV4.undoRecycle(entry.getPwEntryV4(), parent.getPwGroupV4()); } } - public void undoRecycle(PwGroupInterface group, PwGroupInterface parent) { + public void undoRecycle(GroupVersioned group, GroupVersioned parent) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - pwDatabaseV4.undoRecycle(group, parent); - break; - } + pwDatabaseV4.undoRecycle(group.getPwGroupV4(), parent.getPwGroupV4()); } } - public void startManageEntry(PwEntryInterface entry) { + public void startManageEntry(EntryVersioned entry) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - ((PwEntryV4) entry).startToManageFieldReferences(pwDatabaseV4); - break; - } + entry.startToManageFieldReferences(pwDatabaseV4); } } - public void stopManageEntry(PwEntryInterface entry) { + public void stopManageEntry(EntryVersioned entry) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - ((PwEntryV4) entry).stopToManageFieldReferences(); - break; - } + entry.stopToManageFieldReferences(); } } - public void createBackupOf(PwEntryInterface entry) { + public void createBackupOf(EntryVersioned entry) { if (pwDatabaseV4 != null) { - switch (version) { - case V4: - ((PwEntryV4) entry).createBackup(pwDatabaseV4); - break; - } + entry.createBackup(pwDatabaseV4); } } } 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 new file mode 100644 index 000000000..eb7fc77c9 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/EntryVersioned.kt @@ -0,0 +1,341 @@ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable +import com.kunzisoft.keepass.database.ExtraFields +import com.kunzisoft.keepass.database.security.ProtectedString +import java.util.* + +class EntryVersioned : NodeVersioned, PwEntryInterface { + + var pwEntryV3: PwEntryV3? = null + private set + var pwEntryV4: PwEntryV4? = null + private set + + fun updateWith(entry: EntryVersioned) { + this.pwEntryV3?.updateWith(entry.pwEntryV3) + this.pwEntryV4?.updateWith(entry.pwEntryV4) + } + + constructor() + + /** + * Use this constructor to copy an Entry + */ + constructor(entry: EntryVersioned) { + if (entry.pwEntryV3 != null) { + if (this.pwEntryV3 != null) + this.pwEntryV3 = PwEntryV3() + } + if (entry.pwEntryV4 != null) { + if (this.pwEntryV4 != null) + this.pwEntryV4 = PwEntryV4() + } + updateWith(entry) + } + + constructor(entry: PwEntryV3) { + this.pwEntryV4 = null + if (this.pwEntryV3 != null) + this.pwEntryV3 = PwEntryV3() + this.pwEntryV3?.updateWith(entry) + } + + constructor(entry: PwEntryV4) { + this.pwEntryV3 = null + if (this.pwEntryV4 != null) + this.pwEntryV4 = PwEntryV4() + this.pwEntryV4?.updateWith(entry) + } + + constructor(parcel: Parcel) { + pwEntryV3 = parcel.readParcelable(PwEntryV3::class.java.classLoader) + pwEntryV4 = parcel.readParcelable(PwEntryV4::class.java.classLoader) + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeParcelable(pwEntryV3, flags) + dest.writeParcelable(pwEntryV4, flags) + } + + var nodeId: PwNodeId? + get() = pwEntryV4?.nodeId ?: pwEntryV3?.nodeId + set(value) { + pwEntryV3?.nodeId = value + pwEntryV4?.nodeId = value + } + + override var title: String + get() = pwEntryV3?.title ?: pwEntryV4?.title ?: "" + set(value) { + pwEntryV3?.title = value + pwEntryV4?.title = value + } + + override var icon: PwIcon + get() = pwEntryV3?.icon ?: pwEntryV4?.icon ?: PwIconStandard() + set(value) { + pwEntryV3?.icon = value + pwEntryV4?.icon = value + } + + override val type: Type + get() = Type.ENTRY + + override var parent: GroupVersioned? + get() { + pwEntryV3?.parent?.let { + return GroupVersioned(it) + } + pwEntryV4?.parent?.let { + return GroupVersioned(it) + } + return null + } + set(value) { + pwEntryV3?.parent = value?.pwGroupV3 + pwEntryV4?.parent = value?.pwGroupV4 + } + + override fun containsParent(): Boolean { + return pwEntryV3?.containsParent() ?: pwEntryV4?.containsParent() ?: false + } + + override fun touch(modified: Boolean, touchParents: Boolean) { + pwEntryV3?.touch(modified, touchParents) + pwEntryV4?.touch(modified, touchParents) + } + + override fun isContainedIn(container: GroupVersioned): Boolean { + return pwEntryV3?.isContainedIn(container.pwGroupV3) ?: pwEntryV4?.isContainedIn(container.pwGroupV4) ?: false + } + + override val isSearchingEnabled: Boolean + get() = pwEntryV3?.isSearchingEnabled ?: pwEntryV4?.isSearchingEnabled ?: false + + override fun getLastModificationTime(): PwDate? { + return pwEntryV3?.lastModificationTime ?: pwEntryV4?.lastModificationTime + } + + override fun setLastModificationTime(date: PwDate) { + pwEntryV3?.lastModificationTime = date + pwEntryV4?.lastModificationTime = date + } + + override fun getCreationTime(): PwDate? { + return pwEntryV3?.creationTime ?: pwEntryV4?.creationTime + } + + override fun setCreationTime(date: PwDate) { + pwEntryV3?.creationTime = date + pwEntryV4?.creationTime = date + } + + override fun getLastAccessTime(): PwDate? { + return pwEntryV3?.lastAccessTime ?: pwEntryV4?.lastAccessTime + } + + override fun setLastAccessTime(date: PwDate) { + pwEntryV3?.lastAccessTime = date + pwEntryV4?.lastAccessTime = date + } + + override fun getExpiryTime(): PwDate? { + return pwEntryV3?.expiryTime ?: pwEntryV4?.expiryTime + } + + override fun setExpiryTime(date: PwDate) { + pwEntryV3?.expiryTime = date + pwEntryV4?.expiryTime = date + } + + override fun isExpires(): Boolean { + return pwEntryV3?.isExpires ?: pwEntryV4?.isExpires ?: false + } + + override fun setExpires(exp: Boolean) { + pwEntryV3?.isExpires = exp + pwEntryV4?.isExpires = exp + } + + override var username: String + get() = pwEntryV3?.username ?: pwEntryV4?.username ?: "" + set(value) { + pwEntryV3?.username = value + pwEntryV4?.username = value + } + + override var password: String + get() = pwEntryV3?.password ?: pwEntryV4?.password ?: "" + set(value) { + pwEntryV3?.password = value + pwEntryV4?.password = value + } + + override var url: String + get() = pwEntryV3?.url ?: pwEntryV4?.url ?: "" + set(value) { + pwEntryV3?.url = value + pwEntryV4?.url = value + } + + override var notes: String + get() = pwEntryV3?.notes ?: pwEntryV4?.notes ?: "" + set(value) { + pwEntryV3?.notes = value + pwEntryV4?.notes = value + } + + override fun touchLocation() { + pwEntryV3?.touchLocation() + pwEntryV4?.touchLocation() + } + + fun isTan(): Boolean { + return title == PMS_TAN_ENTRY && username.isNotEmpty() + } + + fun getVisualTitle(): String { + return getVisualTitle(isTan(), + title, + username, + url, + nodeId.toString()) + } + + /* + ------------ + V3 Methods + ------------ + */ + + /** + * If it's a node with only meta information like Meta-info SYSTEM Database Color + * @return false by default, true if it's a meta stream + */ + val isMetaStream: Boolean + get() = pwEntryV3?.isMetaStream ?: false + + /* + ------------ + V4 Methods + ------------ + */ + + /** + * Retrieve extra fields to show, key is the label, value is the value of field + * @return Map of label/value + */ + val fields: ExtraFields + get() = pwEntryV4?.fields ?: ExtraFields() + + /** + * To redefine if version of entry allow extra field, + * @return true if entry allows extra field + */ + fun allowExtraFields(): Boolean { + return pwEntryV4?.allowExtraFields() ?: false + } + + /** + * If entry contains extra fields + * @return true if there is extra fields + */ + fun containsCustomFields(): Boolean { + return pwEntryV4?.containsCustomFields() ?: false + } + + /** + * If entry contains extra fields that are protected + * @return true if there is extra fields protected + */ + fun containsCustomFieldsProtected(): Boolean { + return pwEntryV4?.containsCustomFieldsProtected() ?: false + } + + /** + * If entry contains extra fields that are not protected + * @return true if there is extra fields not protected + */ + fun containsCustomFieldsNotProtected(): Boolean { + return pwEntryV4?.containsCustomFieldsNotProtected() ?: false + } + + /** + * Add an extra field to the list (standard or custom) + * @param label Label of field, must be unique + * @param value Value of field + */ + fun addExtraField(label: String, value: ProtectedString) { + pwEntryV4?.addExtraField(label, value) + } + + /** + * Delete all custom fields + */ + fun removeAllCustomFields() { + pwEntryV4?.removeAllCustomFields() + } + + fun startToManageFieldReferences(db: PwDatabaseV4) { + pwEntryV4?.startToManageFieldReferences(db) + } + + fun stopToManageFieldReferences() { + pwEntryV4?.stopToManageFieldReferences() + } + + fun createBackup(db: PwDatabaseV4?) { + pwEntryV4?.createBackup(db) + } + + fun containsCustomData(): Boolean { + return pwEntryV4?.containsCustomData() ?: false + } + + /* + ------------ + Class methods + ------------ + */ + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): EntryVersioned { + return EntryVersioned(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + + val PMS_TAN_ENTRY = "" + + /** + * {@inheritDoc} + * Get the display title from an entry,

+ * [.startManageEntry] and [.stopManageEntry] must be called + * before and after [.getVisualTitle] + */ + fun getVisualTitle(isTan: Boolean, title: String, userName: String, url: String, id: String): String { + return if (isTan) { + "$PMS_TAN_ENTRY $userName" + } else { + if (title.isEmpty()) + if (userName.isEmpty()) + if (url.isEmpty()) + id + else + url + else + userName + else + title + } + } + } +} 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 new file mode 100644 index 000000000..a4e7a3194 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/GroupVersioned.kt @@ -0,0 +1,299 @@ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable +import java.util.* +import kotlin.collections.ArrayList + +class GroupVersioned : NodeVersioned, PwGroupInterface { + + var pwGroupV3: PwGroupV3? = null + private set + var pwGroupV4: PwGroupV4? = null + private set + + fun updateWith(group: GroupVersioned) { + this.pwGroupV3?.updateWith(group.pwGroupV3) + this.pwGroupV4?.updateWith(group.pwGroupV4) + } + + /** + * Use this constructor to copy a Group + */ + constructor(group: GroupVersioned) { + if (group.pwGroupV3 != null) { + if (this.pwGroupV3 == null) + this.pwGroupV3 = PwGroupV3() + } + if (group.pwGroupV4 == null) { + if (this.pwGroupV4 != null) + this.pwGroupV4 = PwGroupV4() + } + updateWith(group) + } + + constructor(group: PwGroupV3) { + this.pwGroupV4 = null + if (this.pwGroupV3 == null) + this.pwGroupV3 = PwGroupV3() + this.pwGroupV3?.updateWith(group) + } + + constructor(group: PwGroupV4) { + this.pwGroupV3 = null + if (this.pwGroupV4 == null) + this.pwGroupV4 = PwGroupV4() + this.pwGroupV4?.updateWith(group) + } + + constructor(parcel: Parcel) { + pwGroupV3 = parcel.readParcelable(PwGroupV3::class.java.classLoader) + pwGroupV4 = parcel.readParcelable(PwGroupV4::class.java.classLoader) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): GroupVersioned { + return GroupVersioned(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeParcelable(pwGroupV3, flags) + dest.writeParcelable(pwGroupV4, flags) + } + + val nodeId: PwNodeId<*>? + get() = pwGroupV4?.nodeId ?: pwGroupV3?.nodeId + + override var title: String + get() = pwGroupV3?.title ?: pwGroupV4?.title ?: "" + set(value) { + pwGroupV3?.title = value + pwGroupV4?.title = value + } + + override var icon: PwIcon + get() = pwGroupV3?.icon ?: pwGroupV4?.icon ?: PwIconStandard() + set(value) { + pwGroupV3?.icon = value + pwGroupV4?.icon = value + } + + override val type: Type + get() = Type.GROUP + + override var parent: GroupVersioned? + get() { + pwGroupV3?.parent?.let { + return GroupVersioned(it) + } + pwGroupV4?.parent?.let { + return GroupVersioned(it) + } + return null + } + set(value) { + pwGroupV3?.parent = value?.pwGroupV3 + pwGroupV4?.parent = value?.pwGroupV4 + } + + override fun containsParent(): Boolean { + return pwGroupV3?.containsParent() ?: pwGroupV4?.containsParent() ?: false + } + + override fun touch(modified: Boolean, touchParents: Boolean) { + pwGroupV3?.touch(modified, touchParents) + pwGroupV4?.touch(modified, touchParents) + } + + override fun isContainedIn(container: GroupVersioned): Boolean { + return pwGroupV3?.isContainedIn(container.pwGroupV3) ?: pwGroupV4?.isContainedIn(container.pwGroupV4) ?: false + } + + override val isSearchingEnabled: Boolean? + get() = pwGroupV3?.isSearchingEnabled ?: pwGroupV4?.isSearchingEnabled ?: false + + override fun getLastModificationTime(): PwDate? { + return pwGroupV3?.lastModificationTime ?: pwGroupV4?.lastModificationTime + } + + override fun setLastModificationTime(date: PwDate) { + pwGroupV3?.lastModificationTime = date + pwGroupV4?.lastModificationTime = date + } + + override fun getCreationTime(): PwDate? { + return pwGroupV3?.creationTime ?: pwGroupV4?.creationTime + } + + override fun setCreationTime(date: PwDate) { + pwGroupV3?.creationTime = date + pwGroupV4?.creationTime = date + } + + override fun getLastAccessTime(): PwDate? { + return pwGroupV3?.lastAccessTime ?: pwGroupV4?.lastAccessTime + } + + override fun setLastAccessTime(date: PwDate) { + pwGroupV3?.lastAccessTime = date + pwGroupV4?.lastAccessTime = date + } + + override fun getExpiryTime(): PwDate? { + return pwGroupV3?.expiryTime ?: pwGroupV4?.expiryTime + } + + override fun setExpiryTime(date: PwDate) { + pwGroupV3?.expiryTime = date + pwGroupV4?.expiryTime = date + } + + override fun isExpires(): Boolean { + return pwGroupV3?.isExpires ?: pwGroupV4?.isExpires ?: false + } + + override fun setExpires(exp: Boolean) { + pwGroupV3?.isExpires = exp + pwGroupV4?.isExpires = exp + } + + override fun getChildGroups(): MutableList { + return ArrayList() // TODO if needed + } + + override fun getChildEntries(): MutableList { + val children = ArrayList() + + pwGroupV3?.getChildEntries()?.forEach { + children.add(EntryVersioned(it)) + } + + pwGroupV4?.getChildEntries()?.forEach { + children.add(EntryVersioned(it)) + } + + return children + } + + /** + * Filter MetaStream entries and return children + * @return List of direct children (one level below) as PwNode + */ + fun getChildEntriesWithoutMetaStream(): List? { + pwGroupV3?.let { + return getChildEntries().filter { !it.isMetaStream } + } + + pwGroupV4?.let { + // No MetasStream in V4 + return getChildEntries() + } + + return null + } + + override fun addChildGroup(group: GroupVersioned) { + group.pwGroupV3?.let { + pwGroupV3?.addChildGroup(it) + } + group.pwGroupV4?.let { + pwGroupV4?.addChildGroup(it) + } + } + + override fun addChildEntry(entry: EntryVersioned) { + entry.pwEntryV3?.let { + pwGroupV3?.addChildEntry(it) + } + entry.pwEntryV4?.let { + pwGroupV4?.addChildEntry(it) + } + } + + override fun removeChildGroup(group: GroupVersioned) { + group.pwGroupV3?.let { + pwGroupV3?.removeChildGroup(it) + } + group.pwGroupV4?.let { + pwGroupV4?.removeChildGroup(it) + } + } + + override fun removeChildEntry(entry: EntryVersioned) { + entry.pwEntryV3?.let { + pwGroupV3?.removeChildEntry(it) + } + entry.pwEntryV4?.let { + pwGroupV4?.removeChildEntry(it) + } + } + + override fun allowAddEntryIfIsRoot(): Boolean { + return pwGroupV3?.allowAddEntryIfIsRoot() ?: pwGroupV4?.allowAddEntryIfIsRoot() ?: false + } + + /* + ------------ + V3 Methods + ------------ + */ + + var nodeIdV3: PwNodeId? + get() = pwGroupV3?.nodeId + set(value) { pwGroupV3?.nodeId = value } + + fun setNodeId(id: PwNodeIdInt) { + pwGroupV3?.nodeId = id + } + + fun getLevel(): Int { + return pwGroupV3?.level ?: -1 + } + + fun setLevel(level: Int) { + pwGroupV3?.level = level + } + + /* + ------------ + V4 Methods + ------------ + */ + + var nodeIdV4: PwNodeId? + get() = pwGroupV4?.nodeId + set(value) { pwGroupV4?.nodeId = value } + + fun setNodeId(id: PwNodeIdUUID) { + pwGroupV4?.nodeId = id + } + + fun setIconStandard(icon: PwIconStandard) { + pwGroupV4?.setIconStandard(icon) + } + + fun setEnableAutoType(enableAutoType: Boolean?) { + pwGroupV4?.enableAutoType = enableAutoType + } + + fun setEnableSearching(enableSearching: Boolean?) { + pwGroupV4?.enableSearching = enableSearching + } + + fun setExpanded(expanded: Boolean) { + pwGroupV4?.isExpanded = expanded + } + + fun containsCustomData(): Boolean { + return pwGroupV4?.containsCustomData() ?: false + } +} 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 new file mode 100644 index 000000000..c0de233e9 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/NodeVersioned.kt @@ -0,0 +1,12 @@ +package com.kunzisoft.keepass.database.element + +interface NodeVersioned: PwNodeInterface + +/** + * Type of available Nodes + */ +enum class Type { + GROUP, ENTRY +} + + diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index 3912fd8d4..b236523b0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -37,7 +37,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.UUID; -public abstract class PwDatabase { +public abstract class PwDatabase { public static final UUID UUID_ZERO = new UUID(0,0); @@ -49,9 +49,8 @@ public abstract class PwDatabase { protected PwIconFactory iconFactory = new PwIconFactory(); - protected PwGroupInterface rootGroup; - protected LinkedHashMap groupIndexes = new LinkedHashMap<>(); - protected LinkedHashMap entryIndexes = new LinkedHashMap<>(); + protected LinkedHashMap groupIndexes = new LinkedHashMap<>(); + protected LinkedHashMap entryIndexes = new LinkedHashMap<>(); public abstract PwVersion getVersion(); @@ -230,17 +229,15 @@ public abstract class PwDatabase { * ------------------------------------- */ + public abstract void populateNodesIndexes(); + public abstract PwNodeId newGroupId(); - public abstract PwGroupInterface createGroup(); + public abstract PwNodeId newEntryId(); - public PwGroupInterface getRootGroup() { - return rootGroup; - } + public abstract Group createGroup(); - public void setRootGroup(PwGroupInterface rootGroup) { - this.rootGroup = rootGroup; - } + public abstract Group getRootGroup(); /* * ------------------------------------- @@ -259,26 +256,26 @@ public abstract class PwDatabase { return groupIndexes.containsKey(id); } - public Collection getGroupIndexes() { + public Collection getGroupIndexes() { return groupIndexes.values(); } - public void setGroupIndexes(List groupList) { + public void setGroupIndexes(List groupList) { this.groupIndexes.clear(); - for (PwGroupInterface currentGroup : groupList) { + for (Group currentGroup : groupList) { this.groupIndexes.put(currentGroup.getNodeId(), currentGroup); } } - public PwGroupInterface getGroupById(PwNodeId id) { + public Group getGroupById(PwNodeId id) { return this.groupIndexes.get(id); } - public void addGroupIndex(PwGroupInterface group) { + public void addGroupIndex(Group group) { this.groupIndexes.put(group.getNodeId(), group); } - public void removeGroupIndex(PwGroupInterface group) { + public void removeGroupIndex(Group group) { this.groupIndexes.remove(group.getNodeId()); } @@ -286,19 +283,23 @@ public abstract class PwDatabase { return groupIndexes.size(); } - public Collection getEntryIndexes() { + public boolean isEntryIdUsed(PwNodeId id) { + return entryIndexes.containsKey(id); + } + + public Collection getEntryIndexes() { return entryIndexes.values(); } - public PwEntryInterface getEntryById(PwNodeId id) { + public Entry getEntryById(PwNodeId id) { return this.entryIndexes.get(id); } - public void addEntryIndex(PwEntryInterface entry) { + public void addEntryIndex(Entry entry) { this.entryIndexes.put(entry.getNodeId(), entry); } - public void removeEntryIndex(PwEntryInterface entry) { + public void removeEntryIndex(Entry entry) { this.entryIndexes.remove(entry.getNodeId()); } @@ -312,7 +313,7 @@ public abstract class PwDatabase { * ------------------------------------- */ - protected void addGroupTo(PwGroupInterface newGroup, @Nullable PwGroupInterface parent) { + protected void addGroupTo(Group newGroup, @Nullable Group parent) { // Add tree to parent tree if (parent != null) parent.addChildGroup(newGroup); @@ -320,7 +321,7 @@ public abstract class PwDatabase { addGroupIndex(newGroup); } - protected void removeGroupFrom(PwGroupInterface groupToRemove, PwGroupInterface parent) { + protected void removeGroupFrom(Group groupToRemove, Group parent) { // Remove tree from parent tree if (parent != null) { parent.removeChildGroup(groupToRemove); @@ -328,7 +329,7 @@ public abstract class PwDatabase { removeGroupIndex(groupToRemove); } - protected void addEntryTo(PwEntryInterface newEntry, @Nullable PwGroupInterface parent) { + protected void addEntryTo(Entry newEntry, @Nullable Group parent) { // Add entry to parent if (parent != null) parent.addChildEntry(newEntry); @@ -336,7 +337,7 @@ public abstract class PwDatabase { addEntryIndex(newEntry); } - protected void removeEntryFrom(PwEntryInterface entryToRemove, PwGroupInterface parent) { + protected void removeEntryFrom(Entry entryToRemove, Group parent) { // Remove entry for parent if (parent != null) { parent.removeChildEntry(entryToRemove); @@ -345,17 +346,17 @@ public abstract class PwDatabase { } // TODO Delete group - public void undoDeleteGroup(PwGroupInterface group, PwGroupInterface origParent) { + public void undoDeleteGroupFrom(Group group, Group origParent) { addGroupTo(group, origParent); } - public void undoDeleteEntryFrom(PwEntryInterface entry, PwGroupInterface origParent) { + public void undoDeleteEntryFrom(Entry entry, Group origParent) { addEntryTo(entry, origParent); } - public abstract boolean isBackup(PwGroupInterface group); + public abstract boolean isBackup(Group group); - public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { + public boolean isGroupSearchable(Group group, boolean omitBackup) { return group != null; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index 7f7fae7ad..07bcaa97e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -29,22 +29,21 @@ import java.io.InputStream; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Random; +import java.util.*; /** * @author Naomaru Itoi * @author Bill Zwicky * @author Dominik Reichl */ -public class PwDatabaseV3 extends PwDatabase { +public class PwDatabaseV3 extends PwDatabase { private static final int DEFAULT_ENCRYPTION_ROUNDS = 300; private int numKeyEncRounds; + protected PwGroupV3 rootGroup; + public PwDatabaseV3() { algorithm = PwEncryptionAlgorithm.AES_Rijndael; numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS; @@ -62,15 +61,60 @@ public class PwDatabaseV3 extends PwDatabase { return list; } - public List getRootGroups() { - List kids = new ArrayList<>(); - for (Map.Entry grp : groupIndexes.entrySet()) { - if (grp.getValue().getLevel() == 0) - kids.add(grp.getValue()); + public List getRootGroups() { + List kids = new ArrayList<>(); + for (Map.Entry group : groupIndexes.entrySet()) { + if (group.getValue().getLevel() == 0) + kids.add(group.getValue()); } return kids; } + private void assignGroupsChildren(PwGroupV3 parent) { + int levelToCheck = parent.getLevel() + 1; + boolean startFromParentPosition = false; + for (PwGroupV3 groupToCheck: getGroupIndexes()) { + if (getRootGroup().getNodeId().equals(parent.getNodeId()) + || groupToCheck.getNodeId().equals(parent.getNodeId())) { + startFromParentPosition = true; + } + if (startFromParentPosition) { + if (groupToCheck.getLevel() < levelToCheck) + break; + else if (groupToCheck.getLevel() == levelToCheck) + parent.addChildGroup(groupToCheck); + } + } + } + + private void assignEntriesChildren(PwGroupV3 parent) { + for (PwEntryV3 entry : getEntryIndexes()) { + if (entry.getParent().getNodeId().equals(parent.getNodeId())) + parent.addChildEntry(entry); + } + } + + private void constructTreeFromIndex(PwGroupV3 currentGroup) { + + assignGroupsChildren(currentGroup); + assignEntriesChildren(currentGroup); + + // set parent in child entries (normally useless but to be sure or to update parent metadata) + for (PwEntryV3 childEntry : currentGroup.getChildEntries()) { + childEntry.setParent(currentGroup); + } + // recursively construct child groups + for (PwGroupV3 childGroup : currentGroup.getChildGroups()) { + childGroup.setParent(currentGroup); + constructTreeFromIndex(childGroup); + } + } + + @Override + public void populateNodesIndexes() { + constructTreeFromIndex(getRootGroup()); + } + /** * Generates an unused random tree id * @@ -86,6 +130,21 @@ public class PwDatabaseV3 extends PwDatabase { return newId; } + /** + * Generates an unused random tree id + * + * @return new tree id + */ + @Override + public PwNodeIdUUID newEntryId() { + PwNodeIdUUID newId; + do { + newId = new PwNodeIdUUID(UUID.randomUUID()); + } while (isEntryIdUsed(newId)); + + return newId; + } + @Override public byte[] getMasterKey(String key, InputStream keyInputStream) throws InvalidKeyFileException, IOException { @@ -158,7 +217,16 @@ public class PwDatabaseV3 extends PwDatabase { public PwGroupV3 createGroup() { return new PwGroupV3(); } - + + public void setRootGroup(PwGroupV3 rootGroup) { + this.rootGroup = rootGroup; + } + + @Override + public PwGroupV3 getRootGroup() { + return rootGroup; + } + // TODO: This could still be refactored cleaner public void copyEncrypted(byte[] buf, int offset, int size) { // No-op @@ -170,7 +238,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public boolean isBackup(PwGroupInterface group) { + public boolean isBackup(PwGroupV3 group) { while (group != null) { if (group.getLevel() == 0 && group.getTitle().equalsIgnoreCase("Backup")) { return true; @@ -181,7 +249,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { + public boolean isGroupSearchable(PwGroupV3 group, boolean omitBackup) { if (!super.isGroupSearchable(group, omitBackup)) { return false; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index c4933f595..fdf31e880 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -43,13 +43,15 @@ import java.security.NoSuchAlgorithmException; import java.util.*; -public class PwDatabaseV4 extends PwDatabase { +public class PwDatabaseV4 extends PwDatabase { private static final String TAG = PwDatabaseV4.class.getName(); private static final int DEFAULT_HISTORY_MAX_ITEMS = 10; // -1 unlimited private static final long DEFAULT_HISTORY_MAX_SIZE = 6 * 1024 * 1024; // -1 unlimited private static final String RECYCLEBIN_NAME = "RecycleBin"; + private PwGroupV4 rootGroup; + private byte[] hmacKey; private UUID dataCipher = AesEngine.CIPHER_UUID; private CipherEngine dataEngine = new AesEngine(); @@ -93,24 +95,6 @@ public class PwDatabaseV4 extends PwDatabase { public PwDatabaseV4() {} - public void populateNodeIndex() { - PwGroupInterface.doForEachChildAndForRoot(getRootGroup(), - new EntryHandler() { - @Override - public boolean operate(PwEntryInterface entry) { - addEntryIndex(entry); - return true; - } - }, - new GroupHandler() { - @Override - public boolean operate(PwGroupInterface group) { - addGroupIndex(group); - return true; - } - }); - } - @Override public PwVersion getVersion() { return PwVersion.V4; @@ -486,6 +470,25 @@ public class PwDatabaseV4 extends PwDatabase { return null; } + @Override + public void populateNodesIndexes() { + getRootGroup().doForEachChildAndForIt( + new NodeHandler() { + @Override + public boolean operate(PwEntryV4 entry) { + addEntryIndex(entry); + return true; + } + }, + new NodeHandler() { + @Override + public boolean operate(PwGroupV4 group) { + addGroupIndex(group); + return true; + } + }); + } + @Override public PwNodeIdUUID newGroupId() { PwNodeIdUUID newId; @@ -496,13 +499,32 @@ public class PwDatabaseV4 extends PwDatabase { return newId; } + @Override + public PwNodeIdUUID newEntryId() { + PwNodeIdUUID newId; + do { + newId = new PwNodeIdUUID(UUID.randomUUID()); + } while (isEntryIdUsed(newId)); + + return newId; + } + @Override public PwGroupV4 createGroup() { return new PwGroupV4(); } - @Override - public boolean isBackup(PwGroupInterface group) { + public void setRootGroup(PwGroupV4 rootGroup) { + this.rootGroup = rootGroup; + } + + @Override + public PwGroupV4 getRootGroup() { + return rootGroup; + } + + @Override + public boolean isBackup(PwGroupV4 group) { if (!recycleBinEnabled) { return false; } @@ -564,11 +586,11 @@ public class PwDatabaseV4 extends PwDatabase { * @param group Group to remove * @return true if group can be recycle, false elsewhere */ - public boolean canRecycle(PwGroupInterface group) { + public boolean canRecycle(PwGroupV4 group) { if (!recycleBinEnabled) { return false; } - PwGroupInterface recycle = getRecycleBin(); + PwGroupV4 recycle = getRecycleBin(); return (recycle == null) || (!group.isContainedIn(recycle)); } @@ -577,50 +599,44 @@ public class PwDatabaseV4 extends PwDatabase { * @param entry Entry to remove * @return true if entry can be recycle, false elsewhere */ - public boolean canRecycle(PwEntryInterface entry) { + public boolean canRecycle(PwEntryV4 entry) { if (!recycleBinEnabled) { return false; } - PwGroupInterface parent = entry.getParent(); + PwGroupV4 parent = entry.getParent(); return (parent != null) && canRecycle(parent); } - public void recycle(PwGroupInterface group) { + public void recycle(PwGroupV4 group) { ensureRecycleBin(); - PwGroupInterface parent = group.getParent(); - removeGroupFrom(group, parent); + removeGroupFrom(group, group.getParent()); - PwGroupInterface recycleBin = getRecycleBin(); - addGroupTo(group, recycleBin); + addGroupTo(group, getRecycleBin()); // TODO ? group.touchLocation(); } - public void recycle(PwEntryInterface entry) { + public void recycle(PwEntryV4 entry) { ensureRecycleBin(); - PwGroupInterface parent = entry.getParent(); - removeEntryFrom(entry, parent); + removeEntryFrom(entry, entry.getParent()); - PwGroupInterface recycleBin = getRecycleBin(); - addEntryTo(entry, recycleBin); + addEntryTo(entry, getRecycleBin()); entry.touchLocation(); } - public void undoRecycle(PwGroupInterface group, PwGroupInterface origParent) { + public void undoRecycle(PwGroupV4 group, PwGroupV4 origParent) { - PwGroupInterface recycleBin = getRecycleBin(); - removeGroupFrom(group, recycleBin); + removeGroupFrom(group, getRecycleBin()); addGroupTo(group, origParent); } - public void undoRecycle(PwEntryInterface entry, PwGroupInterface origParent) { + public void undoRecycle(PwEntryV4 entry, PwGroupV4 origParent) { - PwGroupInterface recycleBin = getRecycleBin(); - removeEntryFrom(entry, recycleBin); + removeEntryFrom(entry, getRecycleBin()); addEntryTo(entry, origParent); } @@ -634,18 +650,18 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - protected void removeEntryFrom(PwEntryInterface entryToRemove, PwGroupInterface parent) { + protected void removeEntryFrom(PwEntryV4 entryToRemove, PwGroupV4 parent) { super.removeEntryFrom(entryToRemove, parent); deletedObjects.add(new PwDeletedObject((UUID) entryToRemove.getNodeId().getId())); } @Override - public void undoDeleteEntryFrom(PwEntryInterface entry, PwGroupInterface origParent) { + public void undoDeleteEntryFrom(PwEntryV4 entry, PwGroupV4 origParent) { super.undoDeleteEntryFrom(entry, origParent); deletedObjects.remove(new PwDeletedObject((UUID) entry.getNodeId().getId())); } - public PwGroupInterface getRecycleBin() { // TODO delete recycle bin preference + public PwGroupV4 getRecycleBin() { // TODO delete recycle bin preference if (recycleBinUUID == null) { return null; } @@ -675,7 +691,7 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public boolean isGroupSearchable(PwGroupInterface group, boolean omitBackup) { + public boolean isGroupSearchable(PwGroupV4 group, boolean omitBackup) { if (!super.isGroupSearchable(group, omitBackup)) { return false; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java index 065fb8912..f11765814 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java @@ -23,8 +23,7 @@ import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; import com.kunzisoft.keepass.database.CrsAlgorithm; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; +import com.kunzisoft.keepass.database.NodeHandler; import com.kunzisoft.keepass.database.PwCompressionAlgorithm; import com.kunzisoft.keepass.database.exception.InvalidDBVersionException; import com.kunzisoft.keepass.stream.CopyInputStream; @@ -32,6 +31,8 @@ import com.kunzisoft.keepass.stream.HmacBlockStream; import com.kunzisoft.keepass.stream.LEDataInputStream; import com.kunzisoft.keepass.utils.Types; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -40,9 +41,6 @@ import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - public class PwDbHeaderV4 extends PwDbHeader { public static final int DBSIG_PRE2 = 0xB54BFB66; public static final int DBSIG_2 = 0xB54BFB67; @@ -109,12 +107,12 @@ public class PwDbHeaderV4 extends PwDbHeader { this.version = version; } - private class GroupHasCustomData extends GroupHandler { + private class GroupHasCustomData extends NodeHandler { boolean hasCustomData = false; @Override - public boolean operate(PwGroupInterface group) { + public boolean operate(PwGroupV4 group) { if (group == null) { return true; } @@ -127,12 +125,12 @@ public class PwDbHeaderV4 extends PwDbHeader { } } - private class EntryHasCustomData extends EntryHandler { + private class EntryHasCustomData extends NodeHandler { boolean hasCustomData = false; @Override - public boolean operate(PwEntryInterface entry) { + public boolean operate(PwEntryV4 entry) { if (entry == null) { return true; } @@ -164,7 +162,7 @@ public class PwDbHeaderV4 extends PwDbHeader { if (databaseV4.getRootGroup() == null ) { return PwDbHeaderV4.FILE_VERSION_32_3; } - PwGroupInterface.doForEachChildAndForRoot(databaseV4.getRootGroup(), entryHandler, groupHandler); + databaseV4.getRootGroup().doForEachChildAndForIt(entryHandler, groupHandler); if (groupHandler.hasCustomData || entryHandler.hasCustomData) { return PwDbHeaderV4.FILE_VERSION_32_4; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.kt new file mode 100644 index 000000000..1b27ce15d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntry.kt @@ -0,0 +1,17 @@ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import java.util.* + +abstract class PwEntry + < + ParentGroup: PwGroupInterface, + Entry: PwEntryInterface + > + : PwNode, PwEntryInterface { + + constructor() : super() + + constructor(parcel: Parcel) : super(parcel) + +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java deleted file mode 100644 index f0896c663..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.kunzisoft.keepass.database.element; - -import com.kunzisoft.keepass.database.ExtraFields; -import com.kunzisoft.keepass.database.SmallTimeInterface; -import com.kunzisoft.keepass.database.security.ProtectedString; - -public interface PwEntryInterface extends PwNodeInterface, SmallTimeInterface { - - String getTitle(); - void setTitle(String title); - - String getUsername(); - void setUsername(String user); - - String getPassword(); - void setPassword(String pass); - - String getUrl(); - void setUrl(String url); - - String getNotes(); - void setNotes(String notes); - - - /** - * To redefine if version of entry allow extra field, - * @return true if entry allows extra field - */ - boolean allowExtraFields(); - - /** - * Retrieve extra fields to show, key is the label, value is the value of field - * @return Map of label/value - */ - ExtraFields getFields(); - - /** - * If entry contains extra fields - * @return true if there is extra fields - */ - boolean containsCustomFields(); - /** - * If entry contains extra fields that are protected - * @return true if there is extra fields protected - */ - boolean containsCustomFieldsProtected(); - - /** - * If entry contains extra fields that are not protected - * @return true if there is extra fields not protected - */ - boolean containsCustomFieldsNotProtected(); - - /** - * Add an extra field to the list (standard or custom) - * @param label Label of field, must be unique - * @param value Value of field - */ - void addExtraField(String label, ProtectedString value); - - /** - * Delete all custom fields - */ - void removeAllCustomFields(); - - /** - * If it's a node with only meta information like Meta-info SYSTEM Database Color - * @return false by default, true if it's a meta stream - */ - boolean isMetaStream(); - - void touchLocation(); - - PwEntryInterface duplicate(); - - String PMS_TAN_ENTRY = ""; - - static boolean isTan(PwEntryInterface entry) { - return entry.getTitle().equals(PMS_TAN_ENTRY) - && (entry.getUsername().length() > 0); - } - - /** - * {@inheritDoc} - * Get the display title from an entry,
- * {@link #startManageEntry()} and {@link #stopManageEntry()} must be called - * before and after {@link #getVisualTitle(PwEntryInterface entry)} - */ - static String getVisualTitle(boolean isTan, String title, String userName, String url, String id) { - if ( isTan ) { - return PMS_TAN_ENTRY + " " + userName; - } else { - if (title.isEmpty()) - if (userName.isEmpty()) - if (url.isEmpty()) - return id; - else - return url; - else - return userName; - else - return title; - } - } - - static String getVisualTitle(PwEntryInterface entry) { - return getVisualTitle(PwEntryInterface.isTan(entry), - entry.getTitle(), - entry.getUsername(), - entry.getUrl(), - entry.getNodeId().toString()); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt new file mode 100644 index 000000000..ea6c72a4b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt @@ -0,0 +1,14 @@ +package com.kunzisoft.keepass.database.element + +interface PwEntryInterface : PwNodeInterface { + + var username: String + + var password: String + + var url: String + + var notes: String + + fun touchLocation() +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index f2ca42a16..f5b3c0195 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -44,10 +44,8 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -import com.kunzisoft.keepass.database.ExtraFields; -import com.kunzisoft.keepass.database.security.ProtectedString; - import java.io.UnsupportedEncodingException; +import java.util.Arrays; import java.util.UUID; @@ -72,7 +70,7 @@ import java.util.UUID; * @author Dominik Reichl * @author Jeremy Jamet */ -public class PwEntryV3 extends PwNode implements PwEntryInterface { +public class PwEntryV3 extends PwEntry { /** Size of byte buffer needed to hold this struct. */ private static final String PMS_ID_BINDESC = "bin-stream"; @@ -109,6 +107,11 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { parcel.readByteArray(binaryData); } + @Override + protected PwGroupV3 readParentParcelable(Parcel parcel) { + return parcel.readParcelable(PwGroupV3.class.getClassLoader()); + } + @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -179,11 +182,6 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { return newEntry; } - @Override - public PwEntryInterface duplicate() { - return clone(); - } - @Override public Type getType() { return Type.ENTRY; @@ -192,7 +190,7 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { public void setNewParent(int groupId) { PwGroupV3 pwGroupV3 = new PwGroupV3(); pwGroupV3.setNodeId(new PwNodeIdInt(groupId)); - this.parent = pwGroupV3; + setParent(pwGroupV3); } @Override @@ -300,9 +298,8 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { } // Determine if this is a MetaStream entry - @Override public boolean isMetaStream() { - if (binaryData == new byte[0]) return false; + if (Arrays.equals(binaryData, new byte[0])) return false; if (additional.isEmpty()) return false; if (!binaryDesc.equals(PMS_ID_BINDESC)) return false; if (title.isEmpty()) return false; @@ -311,50 +308,14 @@ public class PwEntryV3 extends PwNode implements PwEntryInterface { if (!username.equals(PMS_ID_USER)) return false; if (url.isEmpty()) return false; if (!url.equals(PMS_ID_URL)) return false; - return icon.isMetaStreamIcon(); + return getIcon().isMetaStreamIcon(); } @Override - public boolean isSearchingEnabled() { + public Boolean isSearchingEnabled() { return false; } - @Override - public boolean containsCustomData() { - return false; - } - @Override public void touchLocation() {} - - @Override - public boolean allowExtraFields() { - return false; - } - - @Override - public ExtraFields getFields() { - return new ExtraFields(); // TODO Nullable - } - - @Override - public boolean containsCustomFields() { - return getFields().containsCustomFields(); - } - - @Override - public boolean containsCustomFieldsProtected() { - return getFields().containsCustomFieldsProtected(); - } - - @Override - public boolean containsCustomFieldsNotProtected() { - return getFields().containsCustomFieldsNotProtected(); - } - - @Override - public void addExtraField(String label, ProtectedString value) {} - - @Override - public void removeAllCustomFields() {} } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 067d0e8ca..4f90fd83e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import android.support.annotation.NonNull; import com.kunzisoft.keepass.database.AutoType; import com.kunzisoft.keepass.database.ExtraFields; import com.kunzisoft.keepass.database.ITimeLogger; @@ -36,7 +37,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.UUID; -public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInterface { +public class PwEntryV4 extends PwEntry implements ITimeLogger { public static final String STR_TITLE = "Title"; public static final String STR_USERNAME = "UserName"; @@ -90,6 +91,11 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte tags = parcel.readString(); } + @Override + protected PwGroupV4 readParentParcelable(Parcel parcel) { + return parcel.readParcelable(PwGroupV4.class.getClassLoader()); + } + @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -167,11 +173,6 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte return newEntry; } - @Override - public PwEntryInterface duplicate() { - return clone(); - } - @Override public Type getType() { return Type.ENTRY; @@ -306,34 +307,31 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte } public PwIcon getIconStandard() { - return icon; + return getIcon(); } public void setIconStandard(PwIconStandard icon) { - this.icon = icon; + super.setIcon(icon); this.customIcon = PwIconCustom.ZERO; } - @Override public boolean allowExtraFields() { return true; } + @NonNull public ExtraFields getFields() { return fields; } - @Override public boolean containsCustomFields() { return getFields().containsCustomFields(); } - @Override public boolean containsCustomFieldsProtected() { return getFields().containsCustomFieldsProtected(); } - @Override public boolean containsCustomFieldsNotProtected() { return getFields().containsCustomFieldsNotProtected(); } @@ -342,7 +340,6 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte fields.putProtectedString(label, value); } - @Override public void removeAllCustomFields() { fields.removeAllCustomFields(); } @@ -458,7 +455,7 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte } public void createBackup(PwDatabaseV4 db) { - PwEntryV4 copy = clone(); + PwEntryV4 copy = new PwEntryV4(); // Clone copy.history = new ArrayList<>(); history.add(copy); @@ -526,9 +523,9 @@ public class PwEntryV4 extends PwNode implements ITimeLogger, PwEntryInte parentGroupLastMod = new PwDate(); } - public boolean isSearchingEnabled() { - if (parent != null) { - return parent.isSearchingEnabled(); + public Boolean isSearchingEnabled() { + if (getParent() != null) { + return getParent().isSearchingEnabled(); } return PwGroupV4.DEFAULT_SEARCHING_ENABLED; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java deleted file mode 100644 index 715447eeb..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -import java.util.ArrayList; -import java.util.List; - -public abstract class PwGroup extends PwNode implements PwGroupInterface { - - private String title = ""; - transient private List childGroups = new ArrayList<>(); - transient private List childEntries = new ArrayList<>(); - - public PwGroup() { - super(); - } - - public PwGroup(Parcel in) { - super(in); - title = in.readString(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeString(title); - } - - protected void updateWith(PwGroup source) { - super.updateWith(source); - title = source.title; - } - - @Override - public String getTitle() { - return title; - } - - @Override - public void setTitle(String name) { - this.title = name; - } - - @Override - public List getChildGroups() { - return childGroups; - } - - @Override - public List getChildEntries() { - return childEntries; - } - - @Override - public void addChildGroup(PwGroupInterface group) { - this.childGroups.add(group); - } - - @Override - public void addChildEntry(PwEntryInterface entry) { - this.childEntries.add(entry); - } - - @Override - public void removeChildGroup(PwGroupInterface group) { - this.childGroups.remove(group); - } - - @Override - public void removeChildEntry(PwEntryInterface entry) { - this.childEntries.remove(entry); - } - - @Override - public int getLevel() { - return -1; - } - - @Override - public void setLevel(int level) { - // Do nothing here - } - - @Override - public List getChildrenWithoutMetastream() { - List children = new ArrayList<>(childGroups); - for(PwEntryInterface child : childEntries) { - if (!child.isMetaStream()) - children.add(child); - } - return children; - } - - @Override - public String toString() { - return getTitle(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.kt new file mode 100644 index 000000000..2a0ccf13e --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroup.kt @@ -0,0 +1,67 @@ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import java.util.* + +abstract class PwGroup + < + Id, + Group: PwGroupInterface, + Entry: PwEntryInterface + > + : PwNode, PwGroupInterface { + + private var titleGroup = "" + @Transient + private val childGroups = ArrayList() + @Transient + private val childEntries = ArrayList() + + constructor() : super() + + constructor(parcel: Parcel) : super(parcel) { + titleGroup = parcel.readString() + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeString(titleGroup) + } + + protected fun updateWith(source: PwGroup) { + super.updateWith(source) + titleGroup = source.titleGroup + } + + override var title: String + get() = titleGroup + set(value) { titleGroup = value } + + override fun getChildGroups(): MutableList { + return childGroups + } + + override fun getChildEntries(): MutableList { + return childEntries + } + + override fun addChildGroup(group: Group) { + this.childGroups.add(group) + } + + override fun addChildEntry(entry: Entry) { + this.childEntries.add(entry) + } + + override fun removeChildGroup(group: Group) { + this.childGroups.remove(group) + } + + override fun removeChildEntry(entry: Entry) { + this.childEntries.remove(entry) + } + + override fun toString(): String { + return titleGroup + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java deleted file mode 100644 index add78a1df..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.kunzisoft.keepass.database.element; - -import android.support.annotation.NonNull; - -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; - -import java.util.List; - -public interface PwGroupInterface extends PwNodeInterface { - - int getLevel(); - - void setLevel(int level); - - List getChildGroups(); - - List getChildEntries(); - - void addChildGroup(PwGroupInterface group); - - void addChildEntry(PwEntryInterface entry); - - void removeChildGroup(PwGroupInterface group); - - void removeChildEntry(PwEntryInterface entry); - - /** - * Filter MetaStream entries and return children - * @return List of direct children (one level below) as PwNode - */ - List getChildrenWithoutMetastream(); - - boolean allowAddEntryIfIsRoot(); - - PwGroupInterface duplicate(); - - static void doForEachChildAndForRoot(@NonNull PwGroupInterface root, - EntryHandler entryHandler, - GroupHandler groupHandler) { - doForEachChild(root, entryHandler, groupHandler); - groupHandler.operate(root); - } - - static boolean doForEachChild(@NonNull PwGroupInterface root, - EntryHandler entryHandler, - GroupHandler groupHandler) { - for (PwEntryInterface entry : root.getChildEntries()) { - if (!entryHandler.operate(entry)) return false; - } - for (PwGroupInterface group : root.getChildGroups()) { - if ((groupHandler != null) && !groupHandler.operate(group)) return false; - doForEachChild(group, entryHandler, groupHandler); - } - return true; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt new file mode 100644 index 000000000..f5cd9d562 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt @@ -0,0 +1,38 @@ +package com.kunzisoft.keepass.database.element + +import com.kunzisoft.keepass.database.NodeHandler + +interface PwGroupInterface, Entry> : PwNodeInterface { + + fun getChildGroups(): MutableList + + fun getChildEntries(): MutableList + + fun addChildGroup(group: Group) + + fun addChildEntry(entry: Entry) + + fun removeChildGroup(group: Group) + + fun removeChildEntry(entry: Entry) + + fun allowAddEntryIfIsRoot(): Boolean + + fun doForEachChildAndForIt(entryHandler: NodeHandler, + groupHandler: NodeHandler) { + doForEachChild(entryHandler, groupHandler) + groupHandler.operate(this as Group) + } + + fun doForEachChild(entryHandler: NodeHandler, + groupHandler: NodeHandler?): Boolean { + for (entry in this.getChildEntries()) { + if (!entryHandler.operate(entry)) return false + } + for (group in this.getChildGroups()) { + if (groupHandler != null && !groupHandler.operate(group)) return false + group.doForEachChild(entryHandler, groupHandler) + } + return true + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 76a29f3d7..46d5d1a1d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; -public class PwGroupV3 extends PwGroup { +public class PwGroupV3 extends PwGroup { private int level = 0; // short /** Used by KeePass internally, don't use */ @@ -43,6 +43,11 @@ public class PwGroupV3 extends PwGroup { flags = in.readInt(); } + @Override + protected PwGroupV3 readParentParcelable(Parcel parcel) { + return parcel.readParcelable(PwGroupV3.class.getClassLoader()); + } + @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -78,41 +83,31 @@ public class PwGroupV3 extends PwGroup { return (PwGroupV3) super.clone(); } - @Override - public PwGroupInterface duplicate() { - return clone(); - } - @Override public Type getType() { return Type.GROUP; } - public void setParent(PwGroupInterface parent) { + public void setParent(PwGroupV3 parent) { super.setParent(parent); - level = parent.getLevel() + 1; + try { + level = parent.getLevel() + 1; + } catch (ClassCastException ignored) {} } @Override - public boolean isSearchingEnabled() { + public Boolean isSearchingEnabled() { return false; } - @Override - public boolean containsCustomData() { - return false; - } - public void setGroupId(int groupId) { this.setNodeId(new PwNodeIdInt(groupId)); } - @Override public int getLevel() { return level; } - @Override public void setLevel(int level) { this.level = level; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 0e3a05c16..14b87d057 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -26,7 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -public class PwGroupV4 extends PwGroup implements ITimeLogger { +public class PwGroupV4 extends PwGroup implements ITimeLogger { public static final boolean DEFAULT_SEARCHING_ENABLED = true; @@ -68,6 +68,11 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger { lastTopVisibleEntry = (UUID) in.readSerializable(); } + @Override + protected PwGroupV4 readParentParcelable(Parcel parcel) { + return parcel.readParcelable(PwGroupV4.class.getClassLoader()); + } + @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); @@ -138,18 +143,13 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger { return newGroup; } - @Override - public PwGroupInterface duplicate() { - return clone(); - } - @Override public Type getType() { return Type.GROUP; } @Override - public void setParent(PwGroupInterface parent) { + public void setParent(PwGroupV4 parent) { super.setParent(parent); locationChangeDate = new PwDate(); } @@ -186,7 +186,7 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger { @Override public PwIcon getIcon() { - if (customIcon == null || customIcon.getUUID().equals(PwDatabase.UUID_ZERO)) { + if (customIcon == null || customIcon.isUnknown()) { // TODO Encapsulate with PwEntryV4 return super.getIcon(); } else { return customIcon; @@ -202,11 +202,11 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger { } public PwIcon getIconStandard() { - return icon; + return getIcon(); } public void setIconStandard(PwIconStandard icon) { // TODO Encapsulate with PwEntryV4 - this.icon = icon; + super.setIcon(icon); this.customIcon = PwIconCustom.ZERO; } @@ -266,16 +266,17 @@ public class PwGroupV4 extends PwGroup implements ITimeLogger { this.lastTopVisibleEntry = lastTopVisibleEntry; } - public boolean isSearchingEnabled() { - PwGroupV4 group = this; + public Boolean isSearchingEnabled() { + + GroupVersioned group = new GroupVersioned(this); while (group != null) { - Boolean search = group.enableSearching; + Boolean search = group.isSearchingEnabled(); if (search != null) { return search; } - group = (PwGroupV4) group.parent; + group = group.getParent(); } - + // If we get to the root tree and its null, default to true return true; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index d3172c22a..03483e307 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -22,19 +22,22 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; - -import com.kunzisoft.keepass.app.App; - import org.joda.time.LocalDate; /** * Abstract class who manage Groups and Entries */ -public abstract class PwNode implements PwNodeInterface, Parcelable, Cloneable { +public abstract class PwNode + < + IdType, + Parent extends PwGroupInterface, + Entry extends PwEntryInterface + > + implements PwNodeInterface, Parcelable { private PwNodeId nodeId = initNodeId(); - protected PwGroupInterface parent = null; - protected PwIcon icon = new PwIconStandard(); + private Parent parent = null; + private PwIcon icon = new PwIconStandard(); protected PwDate creation = new PwDate(); private PwDate lastMod = new PwDate(); private PwDate lastAccess = new PwDate(); @@ -44,32 +47,22 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo protected PwNode() {} - protected PwNode(Parcel in) { - nodeId = in.readParcelable(PwNodeId.class.getClassLoader()); - // TODO better technique ? - try { - PwNodeId pwGroupId = in.readParcelable(PwNodeId.class.getClassLoader()); - parent = App.getDB().getGroupById(pwGroupId); - } catch (Exception e) { - e.printStackTrace(); - } - - icon = in.readParcelable(PwIconStandard.class.getClassLoader()); - creation = in.readParcelable(PwDate.class.getClassLoader()); - lastMod = in.readParcelable(PwDate.class.getClassLoader()); - lastAccess = in.readParcelable(PwDate.class.getClassLoader()); - expireDate = in.readParcelable(PwDate.class.getClassLoader()); + protected PwNode(Parcel parcel) { + nodeId = parcel.readParcelable(PwNodeId.class.getClassLoader()); + parent = readParentParcelable(parcel); + icon = parcel.readParcelable(PwIconStandard.class.getClassLoader()); + creation = parcel.readParcelable(PwDate.class.getClassLoader()); + lastMod = parcel.readParcelable(PwDate.class.getClassLoader()); + lastAccess = parcel.readParcelable(PwDate.class.getClassLoader()); + expireDate = parcel.readParcelable(PwDate.class.getClassLoader()); } + protected abstract Parent readParentParcelable(Parcel parcel); + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(nodeId, flags); - - PwNodeId parentId = null; - if (parent != null) - parentId = parent.getNodeId(); - dest.writeParcelable(parentId, flags); - + dest.writeParcelable(parent, flags); dest.writeParcelable(icon, flags); dest.writeParcelable(creation, flags); dest.writeParcelable(lastMod, flags); @@ -82,7 +75,7 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo return 0; } - protected void updateWith(PwNode source) { + protected void updateWith(PwNode source) { this.nodeId = source.nodeId; this.parent = source.parent; this.icon = source.icon; @@ -114,13 +107,11 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo return getNodeId().getId(); } - @Override public PwNodeId getNodeId() { return nodeId; } - @Override - public void setNodeId(PwNodeId id) { + public void setNodeId(PwNodeId id) { this.nodeId = id; } @@ -136,18 +127,13 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo this.icon = icon; } - /** - * Retrieve the parent node - * @return PwGroup parent as group - */ - public PwGroupInterface getParent() { + @Override + public Parent getParent() { return parent; } - /** - * Assign a parent to this node - */ - public void setParent(PwGroupInterface parent) { + @Override + public void setParent(Parent parent) { this.parent = parent; } @@ -202,8 +188,8 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo } @Override - public boolean isContainedIn(PwGroupInterface container) { - PwGroupInterface cur = this.getParent(); + public boolean isContainedIn(Parent container) { + Parent cur = this.getParent(); while (cur != null) { if (cur.equals(container)) { return true; @@ -222,7 +208,7 @@ public abstract class PwNode implements PwNodeInterface, Parcelable, Clo setLastModificationTime(now); } - PwGroupInterface parent = getParent(); + Parent parent = getParent(); if (touchParents && parent != null) { parent.touch(modified, true); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java deleted file mode 100644 index 51b07977b..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcelable; - -import com.kunzisoft.keepass.database.SmallTimeInterface; - -public interface PwNodeInterface extends SmallTimeInterface, Parcelable { - - PwNodeId getNodeId(); - - void setNodeId(PwNodeId id); - - String getTitle(); - - void setTitle(String title); - - /** - * @return Visual icon - */ - PwIcon getIcon(); - - void setIcon(PwIcon icon); - - /** - * @return Type of Node - */ - PwNodeInterface.Type getType(); - - /** - * Retrieve the parent node - * @return PwGroup parent as group - */ - PwGroupInterface getParent(); - - /** - * Assign a parent to this node - */ - void setParent(PwGroupInterface prt); - - boolean containsParent(); - - void touch(boolean modified, boolean touchParents); - - boolean isContainedIn(PwGroupInterface container); - - boolean isSearchingEnabled(); - - boolean containsCustomData(); - - /** - * Type of available Nodes - */ - enum Type { - GROUP, ENTRY - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt new file mode 100644 index 000000000..aade02266 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt @@ -0,0 +1,32 @@ +package com.kunzisoft.keepass.database.element + +import android.os.Parcelable +import com.kunzisoft.keepass.database.SmallTimeInterface + +interface PwNodeInterface : SmallTimeInterface, Parcelable { + + var title: String + + /** + * @return Visual icon + */ + var icon: PwIcon + + /** + * @return Type of Node + */ + val type: Type + + /** + * Retrieve the parent node + */ + var parent: ParentGroup? + + val isSearchingEnabled: Boolean? + + fun containsParent(): Boolean + + fun touch(modified: Boolean, touchParents: Boolean) + + fun isContainedIn(container: ParentGroup): Boolean +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java deleted file mode 100644 index 4911b9f7a..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.iterator; - -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.search.SearchParameters; -import com.kunzisoft.keepass.database.search.SearchParametersV4; - -import java.util.Iterator; - -public abstract class EntrySearchStringIterator implements Iterator { - - public static EntrySearchStringIterator getInstance(PwEntryInterface e) { - if (e instanceof PwEntryV3) { - return new EntrySearchStringIteratorV3((PwEntryV3) e); - } else if (e instanceof PwEntryV4) { - return new EntrySearchStringIteratorV4((PwEntryV4) e); - } else { - throw new RuntimeException("This should not be possible"); - } - } - - public static EntrySearchStringIterator getInstance(PwEntryInterface e, SearchParameters sp) { - if (e instanceof PwEntryV3) { - return new EntrySearchStringIteratorV3((PwEntryV3) e, sp); - } else if (e instanceof PwEntryV4) { - return new EntrySearchStringIteratorV4((PwEntryV4) e, (SearchParametersV4) sp); - } else { - throw new RuntimeException("This should not be possible"); - } - } - - @Override - public abstract boolean hasNext(); - - @Override - public abstract String next(); - - @Override - public void remove() { - throw new UnsupportedOperationException("This iterator cannot be used to remove strings."); - - } - - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt similarity index 52% rename from app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java rename to app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt index ddd3f5906..c32979c5b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/EntryHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,16 +17,25 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.iterator + +import com.kunzisoft.keepass.database.element.EntryVersioned + +abstract class EntrySearchStringIterator : Iterator { + + companion object { + + fun getInstance(entry: EntryVersioned): EntrySearchStringIterator { + if (entry.pwEntryV3 != null) { + return EntrySearchStringIteratorV3(entry.pwEntryV3) + } + if (entry.pwEntryV4 != null) { + return EntrySearchStringIteratorV4(entry.pwEntryV4!!) + } + + throw RuntimeException("This should not be possible") + } + } -import com.kunzisoft.keepass.database.element.PwEntryInterface; -/** "Delegate" class for operating on each entry when traversing all of - * them - * @author bpellin - * - */ -public abstract class EntryHandler { - public abstract boolean operate(T entry); } - diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java deleted file mode 100644 index 795a752ae..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.iterator; - -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.search.SearchParametersV4; -import com.kunzisoft.keepass.database.security.ProtectedString; - -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.NoSuchElementException; - -public class EntrySearchStringIteratorV4 extends EntrySearchStringIterator { - - private String current; - private Iterator> setIterator; - private SearchParametersV4 sp; - - public EntrySearchStringIteratorV4(PwEntryV4 entry) { - this.sp = SearchParametersV4.DEFAULT; - setIterator = entry.getFields().getListOfAllFields().entrySet().iterator(); - advance(); - } - - public EntrySearchStringIteratorV4(PwEntryV4 entry, SearchParametersV4 sp) { - this.sp = sp; - setIterator = entry.getFields().getListOfAllFields().entrySet().iterator(); - advance(); - } - - @Override - public boolean hasNext() { - return current != null; - } - - @Override - public String next() { - if (current == null) { - throw new NoSuchElementException("Past the end of the list."); - } - - String next = current; - advance(); - return next; - } - - private void advance() { - while (setIterator.hasNext()) { - Entry entry = setIterator.next(); - - String key = entry.getKey(); - - if (searchInField(key)) { - current = entry.getValue().toString(); - return; - } - - } - - current = null; - } - - private boolean searchInField(String key) { - switch (key) { - case PwEntryV4.STR_TITLE: - return sp.searchInTitles; - case PwEntryV4.STR_USERNAME: - return sp.searchInUserNames; - case PwEntryV4.STR_PASSWORD: - return sp.searchInPasswords; - case PwEntryV4.STR_URL: - return sp.searchInUrls; - case PwEntryV4.STR_NOTES: - return sp.searchInNotes; - default: - return sp.searchInOther; - } - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt new file mode 100644 index 000000000..06c3fc341 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.iterator + +import com.kunzisoft.keepass.database.element.PwEntryV4 +import com.kunzisoft.keepass.database.search.SearchParametersV4 +import com.kunzisoft.keepass.database.security.ProtectedString +import java.util.* +import kotlin.collections.Map.Entry + +class EntrySearchStringIteratorV4 : EntrySearchStringIterator { + + private var current: String? = null + private var setIterator: Iterator>? = null + private var sp: SearchParametersV4? = null + + constructor(entry: PwEntryV4) { + this.sp = SearchParametersV4.DEFAULT + setIterator = entry.fields.listOfAllFields.entries.iterator() + advance() + } + + constructor(entry: PwEntryV4, sp: SearchParametersV4) { + this.sp = sp + setIterator = entry.fields.listOfAllFields.entries.iterator() + advance() + } + + override fun hasNext(): Boolean { + return current != null + } + + override fun next(): String { + if (current == null) { + throw NoSuchElementException("Past the end of the list.") + } + + val next = current + advance() + return next!! + } + + private fun advance() { + while (setIterator!!.hasNext()) { + val entry = setIterator!!.next() + + val key = entry.key + + if (searchInField(key)) { + current = entry.value.toString() + return + } + + } + + current = null + } + + private fun searchInField(key: String): Boolean { + when (key) { + PwEntryV4.STR_TITLE -> return sp!!.searchInTitles + PwEntryV4.STR_USERNAME -> return sp!!.searchInUserNames + PwEntryV4.STR_PASSWORD -> return sp!!.searchInPasswords + PwEntryV4.STR_URL -> return sp!!.searchInUrls + PwEntryV4.STR_NOTES -> return sp!!.searchInNotes + else -> return sp!!.searchInOther + } + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index e7e04bb59..e8072b2b1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -226,51 +226,11 @@ public class ImporterV3 extends Importer { pos += 2 + 4 + fieldSize; } - constructTreeFromIndex(databaseToOpen.getRootGroup()); + databaseToOpen.populateNodesIndexes(); return databaseToOpen; } - private void constructTreeFromIndex(PwGroupInterface currentGroup) { - - assignGroupsChildren(currentGroup); - assignEntriesChildren(currentGroup); - - // set parent in child entries (normally useless but to be sure or to update parent metadata) - for (PwEntryInterface childEntry : currentGroup.getChildEntries()) { - childEntry.setParent(currentGroup); - } - // recursively construct child groups - for (PwGroupInterface childGroup : currentGroup.getChildGroups()) { - childGroup.setParent(currentGroup); - constructTreeFromIndex(childGroup); - } - } - - private void assignGroupsChildren(PwGroupInterface parent) { - int levelToCheck = parent.getLevel() + 1; - boolean startFromParentPosition = false; - for (PwGroupInterface groupToCheck: databaseToOpen.getGroupIndexes()) { - if (databaseToOpen.getRootGroup().getNodeId().equals(parent.getNodeId()) - || groupToCheck.getNodeId().equals(parent.getNodeId())) { - startFromParentPosition = true; - } - if (startFromParentPosition) { - if (groupToCheck.getLevel() < levelToCheck) - break; - else if (groupToCheck.getLevel() == levelToCheck) - parent.addChildGroup(groupToCheck); - } - } - } - - private void assignEntriesChildren(PwGroupInterface parent) { - for (PwEntryInterface entry : databaseToOpen.getEntryIndexes()) { - if (entry.getParent().getNodeId().equals(parent.getNodeId())) - parent.addChildEntry(entry); - } - } - /** * Parse and save one record from binary file. * @param buf diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index c80724dd3..6c1baebe7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -173,7 +173,7 @@ public class ImporterV4 extends Importer { ReadXmlStreamed(isXml); - mDatabase.populateNodeIndex(); + mDatabase.populateNodesIndexes(); return mDatabase; } @@ -291,13 +291,12 @@ public class ImporterV4 extends Importer { private String entryCustomDataValue = null; private void ReadXmlStreamed(InputStream readerStream) throws IOException, InvalidDBException { - - try { - ReadDocumentStreamed(CreatePullParser(readerStream)); - } catch (XmlPullParserException e) { - e.printStackTrace(); - throw new IOException(e.getLocalizedMessage()); - } + try { + ReadDocumentStreamed(CreatePullParser(readerStream)); + } catch (XmlPullParserException e) { + e.printStackTrace(); + throw new IOException(e.getLocalizedMessage()); + } } private static XmlPullParser CreatePullParser(InputStream readerStream) throws XmlPullParserException { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java index 4b72e00ac..208494065 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java @@ -20,35 +20,22 @@ package com.kunzisoft.keepass.database.save; import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwDbHeader; -import com.kunzisoft.keepass.database.element.PwDbHeaderV3; -import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwGroupV3; +import com.kunzisoft.keepass.database.element.*; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.stream.NullOutputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.DigestOutputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; - import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.*; +import java.util.ArrayList; +import java.util.List; public class PwDbV3Output extends PwDbOutput { @@ -215,8 +202,8 @@ public class PwDbV3Output extends PwDbOutput { } // Groups - for (PwGroupInterface group: mDatabaseV3.getGroupIndexes()) { - PwGroupOutputV3 pgo = new PwGroupOutputV3((PwGroupV3) group, os); + for (PwGroupV3 group: mDatabaseV3.getGroupIndexes()) { + PwGroupOutputV3 pgo = new PwGroupOutputV3(group, os); try { pgo.output(); } catch (IOException e) { @@ -225,8 +212,8 @@ public class PwDbV3Output extends PwDbOutput { } // Entries - for (PwEntryInterface entry : mDatabaseV3.getEntryIndexes()) { - PwEntryOutputV3 peo = new PwEntryOutputV3((PwEntryV3) (entry), os); + for (PwEntryV3 entry : mDatabaseV3.getEntryIndexes()) { + PwEntryOutputV3 peo = new PwEntryOutputV3(entry, os); try { peo.output(); } catch (IOException e) { @@ -236,20 +223,20 @@ public class PwDbV3Output extends PwDbOutput { } private void sortGroupsForOutput() { - List groupList = new ArrayList<>(); + List groupList = new ArrayList<>(); // Rebuild list according to coalation sorting order removing any orphaned groups - for (PwGroupInterface rootGroup : mDatabaseV3.getRootGroups()) { + for (PwGroupV3 rootGroup : mDatabaseV3.getRootGroups()) { sortGroup(rootGroup, groupList); } mDatabaseV3.setGroupIndexes(groupList); } - private void sortGroup(PwGroupInterface group, List groupList) { + private void sortGroup(PwGroupV3 group, List groupList) { // Add current tree groupList.add(group); // Recurse over children - for (PwGroupInterface childGroup : group.getChildGroups()) { + for (PwGroupV3 childGroup : group.getChildGroups()) { sortGroup(childGroup, groupList); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index a59ef3a00..dcec07d1d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -21,29 +21,14 @@ package com.kunzisoft.keepass.database.save; import android.util.Log; import android.util.Xml; - +import biz.source_code.base64Coder.Base64Coder; import com.kunzisoft.keepass.crypto.CipherFactory; import com.kunzisoft.keepass.crypto.PwStreamCipherFactory; import com.kunzisoft.keepass.crypto.engine.CipherEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; -import com.kunzisoft.keepass.database.AutoType; -import com.kunzisoft.keepass.database.CrsAlgorithm; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; -import com.kunzisoft.keepass.database.ITimeLogger; -import com.kunzisoft.keepass.database.MemoryProtectionConfig; -import com.kunzisoft.keepass.database.PwCompressionAlgorithm; -import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwDatabaseV4XML; -import com.kunzisoft.keepass.database.element.PwDbHeaderV4; -import com.kunzisoft.keepass.database.element.PwDefsV4; -import com.kunzisoft.keepass.database.element.PwDeletedObject; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.element.PwGroupV4; -import com.kunzisoft.keepass.database.element.PwIconCustom; +import com.kunzisoft.keepass.database.*; +import com.kunzisoft.keepass.database.element.*; import com.kunzisoft.keepass.database.exception.PwDbOutputException; import com.kunzisoft.keepass.database.exception.UnknownKDF; import com.kunzisoft.keepass.database.security.ProtectedBinary; @@ -55,28 +40,20 @@ import com.kunzisoft.keepass.utils.DateUtil; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MemUtil; import com.kunzisoft.keepass.utils.Types; - import org.joda.time.DateTime; import org.spongycastle.crypto.StreamCipher; import org.xmlpull.v1.XmlSerializer; +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Stack; -import java.util.UUID; import java.util.zip.GZIPOutputStream; -import javax.crypto.Cipher; -import javax.crypto.CipherOutputStream; - -import biz.source_code.base64Coder.Base64Coder; - public class PwDbV4Output extends PwDbOutput { private static final String TAG = PwDbV4Output.class.getName(); @@ -155,18 +132,16 @@ public class PwDbV4Output extends PwDbOutput { writeMeta(); - PwGroupV4 root = (PwGroupV4) mPM.getRootGroup(); + PwGroupV4 root = mPM.getRootGroup(); xml.startTag(null, PwDatabaseV4XML.ElemRoot); startGroup(root); Stack groupStack = new Stack<>(); groupStack.push(root); - if (!PwGroupInterface.doForEachChild(root, - new EntryHandler() { + if (!root.doForEachChild( + new NodeHandler() { @Override - public boolean operate(PwEntryInterface entryInterface) { - PwEntryV4 entry = (PwEntryV4) entryInterface; - + public boolean operate(PwEntryV4 entry) { try { writeEntry(entry, false); } catch (IOException ex) { @@ -176,11 +151,9 @@ public class PwDbV4Output extends PwDbOutput { return true; } }, - new GroupHandler() { + new NodeHandler() { @Override - public boolean operate(PwGroupInterface groupInterface) { - PwGroupV4 group = (PwGroupV4) groupInterface; - + public boolean operate(PwGroupV4 group) { while (true) { try { if (group.getParent() == groupStack.peek()) { @@ -338,7 +311,7 @@ public class PwDbV4Output extends PwDbOutput { private void startGroup(PwGroupV4 group) throws IllegalArgumentException, IllegalStateException, IOException { xml.startTag(null, PwDatabaseV4XML.ElemGroup); - writeObject(PwDatabaseV4XML.ElemUuid, group.getNodeId().getId()); + writeObject(PwDatabaseV4XML.ElemUuid, group.getId()); writeObject(PwDatabaseV4XML.ElemName, group.getTitle()); writeObject(PwDatabaseV4XML.ElemNotes, group.getNotes()); writeObject(PwDatabaseV4XML.ElemIcon, group.getIconStandard().getIconId()); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java index 972f1bb01..137ab94fa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java @@ -20,7 +20,6 @@ package com.kunzisoft.keepass.database.save; import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.element.PwGroupV3; import com.kunzisoft.keepass.stream.LEDataOutputStream; import com.kunzisoft.keepass.utils.Types; @@ -80,7 +79,7 @@ public class PwEntryOutputV3 { // Group ID mOS.write(GROUPID_FIELD_TYPE); mOS.write(LONG_FOUR); - mOS.write(LEDataOutputStream.writeIntBuf(((PwGroupV3) mPE.getParent()).getNodeId().getId())); + mOS.write(LEDataOutputStream.writeIntBuf(mPE.getParent().getNodeId().getId())); // Image ID mOS.write(IMAGEID_FIELD_TYPE); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index 9e3668d39..ec1270a50 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -19,11 +19,11 @@ */ package com.kunzisoft.keepass.database.search; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.element.PwEntryInterface; +import com.kunzisoft.keepass.database.NodeHandler; import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; +import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4; import com.kunzisoft.keepass.utils.StrUtil; import com.kunzisoft.keepass.utils.UuidUtil; @@ -31,20 +31,20 @@ import java.util.Date; import java.util.List; import java.util.Locale; -public class EntrySearchHandlerV4 extends EntryHandler { +public class EntrySearchHandlerV4 extends NodeHandler { - private List listStorage; + private List listStorage; protected SearchParametersV4 sp; protected Date now; - public EntrySearchHandlerV4(SearchParametersV4 sp, List listStorage) { + public EntrySearchHandlerV4(SearchParametersV4 sp, List listStorage) { this.listStorage = listStorage; this.sp = sp; this.now = new Date(); } @Override - public boolean operate(PwEntryInterface entry) { + public boolean operate(PwEntryV4 entry) { if (sp.respectEntrySearchingDisabled && !entry.isSearchingEnabled()) { return true; } @@ -64,7 +64,7 @@ public class EntrySearchHandlerV4 extends EntryHandler { } if (sp.searchInGroupNames) { - PwGroupInterface parent = entry.getParent(); + PwGroupV4 parent = entry.getParent(); if (parent != null) { String groupName = parent.getTitle(); if (groupName != null) { @@ -88,8 +88,7 @@ public class EntrySearchHandlerV4 extends EntryHandler { return true; } - private boolean searchID(PwEntryInterface e) { - PwEntryV4 entry = (PwEntryV4) e; + private boolean searchID(PwEntryV4 entry) { if (sp.searchInUUIDs) { String hex = UuidUtil.toHexString(entry.getNodeId().getId()); return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0; @@ -98,8 +97,8 @@ public class EntrySearchHandlerV4 extends EntryHandler { return false; } - private boolean searchStrings(PwEntryInterface entry, String term) { - EntrySearchStringIterator iter = EntrySearchStringIterator.getInstance(entry, sp); + private boolean searchStrings(PwEntryV4 entry, String term) { + EntrySearchStringIterator iter = new EntrySearchStringIteratorV4(entry, sp); while (iter.hasNext()) { String str = iter.next(); if (str != null && str.length() > 0) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java deleted file mode 100644 index 49bf7605d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.search; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.EntryHandler; -import com.kunzisoft.keepass.database.GroupHandler; -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwGroupInterface; -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; - -import java.util.Iterator; -import java.util.Locale; - -public class SearchDbHelper { - - private final Context mContext; - private int incrementEntry = 0; - - public SearchDbHelper(Context context) { - this.mContext = context; - } - - private boolean omitBackup() { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); - return prefs.getBoolean(mContext.getString(R.string.omitbackup_key), mContext.getResources().getBoolean(R.bool.omitbackup_default)); - } - - public PwGroupInterface search(PwDatabase pm, String qStr, int max) { - - PwGroupInterface searchGroup = pm.createGroup(); - searchGroup.setTitle("\"" + qStr + "\""); - - // Search all entries - Locale loc = Locale.getDefault(); - String finalQStr = qStr.toLowerCase(loc); - boolean isOmitBackup = omitBackup(); - - - incrementEntry = 0; - PwGroupInterface.doForEachChild(pm.getRootGroup(), - new EntryHandler() { - @Override - public boolean operate(PwEntryInterface entry) { - if (entryContainsString(entry, finalQStr, loc)) { - searchGroup.addChildEntry(entry); - incrementEntry++; - } - // Stop searching when we have max entries - return incrementEntry <= max; - } - }, - new GroupHandler() { - @Override - public boolean operate(PwGroupInterface group) { - if (pm.isGroupSearchable(group, isOmitBackup)) { - return true; - } - return incrementEntry <= max; - } - }); - - return searchGroup; - } - - private boolean entryContainsString(PwEntryInterface entry, String qStr, Locale loc) { - // Search all strings in the entry - Iterator iterator = EntrySearchStringIterator.getInstance(entry); - while (iterator.hasNext()) { - String str = iterator.next(); - if (str != null && str.length() != 0) { - String lower = str.toLowerCase(loc); - if (lower.contains(qStr)) { - return true; - } - } - } - return false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt new file mode 100644 index 000000000..70c0d594e --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.search + +import android.content.Context +import android.preference.PreferenceManager +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.NodeHandler +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.GroupVersioned +import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator +import java.util.* + +class SearchDbHelper(private val mContext: Context) { + private var incrementEntry = 0 + + private fun omitBackup(): Boolean { + val prefs = PreferenceManager.getDefaultSharedPreferences(mContext) + return prefs.getBoolean(mContext.getString(R.string.omitbackup_key), mContext.resources.getBoolean(R.bool.omitbackup_default)) + } + + fun search(pm: Database, qStr: String, max: Int): GroupVersioned { + + val searchGroup = pm.createGroup() + searchGroup!!.title = "\"" + qStr + "\"" + + // Search all entries + val loc = Locale.getDefault() + val finalQStr = qStr.toLowerCase(loc) + val isOmitBackup = omitBackup() + + + incrementEntry = 0 + pm.rootGroup.doForEachChild( + object : NodeHandler() { + override fun operate(entry: EntryVersioned): Boolean { + if (entryContainsString(entry, finalQStr, loc)) { + searchGroup.addChildEntry(entry) + incrementEntry++ + } + // Stop searching when we have max entries + return incrementEntry <= max + } + }, + object : NodeHandler() { + override fun operate(group: GroupVersioned): Boolean { + return if (pm.isGroupSearchable(group, isOmitBackup)!!) { + true + } else incrementEntry <= max + } + }) + + return searchGroup + } + + private fun entryContainsString(entry: EntryVersioned, qStr: String, loc: Locale): Boolean { + // Search all strings in the entry + val iterator = EntrySearchStringIterator.getInstance(entry) + while (iterator.hasNext()) { + val str = iterator.next() + if (str.isNotEmpty()) { + val lower = str.toLowerCase(loc) + if (lower.contains(qStr)) { + return true + } + } + } + return false + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index 236098279..354e36f56 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -36,7 +36,7 @@ import android.widget.Toast; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.PwGroupInterface; +import com.kunzisoft.keepass.database.element.GroupVersioned; import com.kunzisoft.keepass.database.element.PwIcon; import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION; @@ -79,7 +79,7 @@ public class GroupEditDialogFragment extends DialogFragment return fragment; } - public static GroupEditDialogFragment build(PwGroupInterface group) { + public static GroupEditDialogFragment build(GroupVersioned group) { Bundle bundle = new Bundle(); bundle.putString(KEY_NAME, group.getTitle()); bundle.putParcelable(KEY_ICON, group.getIcon()); diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java index fad1e0541..b5450adca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java @@ -20,7 +20,6 @@ package com.kunzisoft.keepass.utils; import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwEntryInterface; import com.kunzisoft.keepass.database.element.PwEntryV4; import java.util.HashMap; @@ -29,7 +28,7 @@ import java.util.Map; public class SprContextV4 implements Cloneable { public PwDatabaseV4 db; - public PwEntryInterface entry; + public PwEntryV4 entry; public Map refsCache = new HashMap<>(); public SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) { diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index 78bef9cc3..a40c2ac38 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -19,19 +19,14 @@ */ package com.kunzisoft.keepass.utils; +import com.kunzisoft.keepass.database.element.PwEntryV4; +import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.element.PwDatabase; import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwEntryInterface; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupInterface; import com.kunzisoft.keepass.database.search.EntrySearchHandlerV4; import com.kunzisoft.keepass.database.search.SearchParametersV4; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; +import java.util.*; import java.util.Map.Entry; public class SprEngineV4 { @@ -40,17 +35,17 @@ public class SprEngineV4 { private static final String STR_REF_END = "}"; public class TargetResult { - public PwEntryInterface entry; + public PwEntryV4 entry; public char wanted; - public TargetResult(PwEntryInterface entry, char wanted) { + public TargetResult(PwEntryV4 entry, char wanted) { this.entry = entry; this.wanted = wanted; } } - public String compile(String text, PwEntryInterface entry, PwDatabase database) { - SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, (PwEntryV4)entry); + public String compile(String text, PwEntryV4 entry, PwDatabase database) { + SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, entry); return compileInternal(text, ctx, 0); } @@ -80,7 +75,7 @@ public class SprEngineV4 { TargetResult result = findRefTarget(fullRef, ctx); if (result != null) { - PwEntryInterface found = result.entry; + PwEntryV4 found = result.entry; char wanted = result.wanted; if (found != null) { @@ -158,7 +153,7 @@ public class SprEngineV4 { else if (scan == 'O') { sp.searchInOther = true; } else { return null; } - List list = new ArrayList<>(); + List list = new ArrayList<>(); // TODO type parameter searchEntries(ctx.db.getRootGroup(), sp, list); @@ -187,13 +182,13 @@ public class SprEngineV4 { return text; } - private void searchEntries(PwGroupInterface root, SearchParametersV4 sp, List listStorage) { + private void searchEntries(PwGroupV4 root, SearchParametersV4 sp, List listStorage) { if (sp == null) { return; } if (listStorage == null) { return; } List terms = StrUtil.splitSearchTerms(sp.searchString); if (terms.size() <= 1 || sp.regularExpression) { - PwGroupInterface.doForEachChild(root, new EntrySearchHandlerV4(sp, listStorage), null); + root.doForEachChild(new EntrySearchHandlerV4(sp, listStorage), null); return; } @@ -202,9 +197,9 @@ public class SprEngineV4 { Collections.sort(terms, stringLengthComparator); String fullSearch = sp.searchString; - List pg = root.getChildEntries(); + List pg = root.getChildEntries(); for (int i = 0; i < terms.size(); i ++) { - List pgNew = new ArrayList<>(); + List pgNew = new ArrayList<>(); sp.searchString = terms.get(i); @@ -214,14 +209,14 @@ public class SprEngineV4 { negate = sp.searchString.length() > 0; } - if (!PwGroupInterface.doForEachChild(root, new EntrySearchHandlerV4(sp, pgNew), null)) { + if (!root.doForEachChild(new EntrySearchHandlerV4(sp, pgNew), null)) { pg = null; break; } - List complement = new ArrayList<>(); + List complement = new ArrayList<>(); if (negate) { - for (PwEntryInterface entry: pg) { + for (PwEntryV4 entry: pg) { if (!pgNew.contains(entry)) { complement.add(entry); } From 8f77e5874e714496d9b90d522a0f8bb390dfe949 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 3 May 2019 18:33:23 +0200 Subject: [PATCH 101/289] Refactor database import --- .../AssignPasswordInDatabaseRunnable.kt | 8 +- .../database/action/node/AddEntryRunnable.kt | 4 +- .../database/action/node/CopyEntryRunnable.kt | 4 +- .../action/node/DeleteEntryRunnable.kt | 10 +- .../database/action/node/MoveEntryRunnable.kt | 3 +- .../database/action/node/MoveGroupRunnable.kt | 3 +- .../keepass/database/element/Database.java | 845 ------------------ .../keepass/database/element/Database.kt | 702 +++++++++++++++ .../database/element/PwDatabaseV4.java | 2 +- .../keepass/database/element/PwVersion.java | 2 +- .../keepass/database/load/ImporterV3.java | 5 +- .../keepass/database/load/ImporterV4.java | 6 +- .../keepass/database/save/PwDbOutput.java | 13 - .../keepass/database/save/PwDbV4Output.java | 2 +- .../keepass/database/search/SearchDbHelper.kt | 2 +- .../keepass/password/PasswordActivity.java | 23 +- .../settings/MainPreferenceFragment.java | 2 +- .../settings/NestedSettingsFragment.java | 2 +- 18 files changed, 743 insertions(+), 895 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/Database.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt index e0250bebe..2d294b8cd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt @@ -58,7 +58,9 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( System.arraycopy(database.masterKey, 0, mBackupKey!!, 0, mBackupKey!!.size) val uriInputStream = UriUtil.getUriInputStream(context, mKeyFile) - database.retrieveMasterKey(mMasterPassword, uriInputStream) + mMasterPassword?.let { + database.retrieveMasterKey(it, uriInputStream) + } // To save the database super.run() finishRun(true) @@ -75,7 +77,9 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( if (!isSuccess) { // Erase the current master key erase(database.masterKey) - database.masterKey = mBackupKey + mBackupKey?.let { + database.masterKey = it + } } super.onFinishRun(isSuccess, message) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt index 8a245e027..4ee5be7a5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt @@ -41,7 +41,9 @@ class AddEntryRunnable constructor( override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { if (!isSuccess) { - database.removeEntryFrom(mNewEntry, mNewEntry.parent) + mNewEntry.parent?.let { + database.removeEntryFrom(mNewEntry, it) + } } return ActionNodeValues(isSuccess, message, null, mNewEntry) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt index 07c98413b..c8cd4dc26 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt @@ -50,7 +50,9 @@ class CopyEntryRunnable constructor( if (!isSuccess) { // If we fail to save, try to delete the copy try { - database.deleteEntry(mEntryCopied) + mEntryCopied?.let { + database.deleteEntry(it) + } } catch (e: Exception) { Log.i(TAG, "Unable to delete the copied entry") } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt index 93c58eafb..7a0d64bf4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteEntryRunnable.kt @@ -51,10 +51,12 @@ class DeleteEntryRunnable constructor( override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { if (!isSuccess) { - if (mRecycle) { - database.undoRecycle(mEntryToDelete, mParent) - } else { - database.undoDeleteEntry(mEntryToDelete, mParent) + mParent?.let { + if (mRecycle) { + database.undoRecycle(mEntryToDelete, it) + } else { + database.undoDeleteEntry(mEntryToDelete, it) + } } } return ActionNodeValues(isSuccess, message, mEntryToDelete, null) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt index 64ddcad1a..52f41d843 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt @@ -49,7 +49,8 @@ class MoveEntryRunnable constructor( if (!isSuccess) { // If we fail to save, try to remove in the first place try { - database.moveEntry(mEntryToMove, mOldParent) + if (mEntryToMove != null && mOldParent != null) + database.moveEntry(mEntryToMove, mOldParent!!) } catch (e: Exception) { Log.i(TAG, "Unable to replace the entry") } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt index ac1cf96b2..4f5a89ffe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt @@ -57,7 +57,8 @@ class MoveGroupRunnable constructor( if (!isSuccess) { // If we fail to save, try to move in the first place try { - database.moveGroup(mGroupToMove, mOldParent) + if (mGroupToMove != null && mOldParent != null) + database.moveGroup(mGroupToMove, mOldParent!!) } catch (e: Exception) { Log.i(TAG, "Unable to replace the group") } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java deleted file mode 100644 index 0511866b8..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.java +++ /dev/null @@ -1,845 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.content.Context; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.util.Log; -import android.webkit.URLUtil; -import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; -import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; -import com.kunzisoft.keepass.database.NodeHandler; -import com.kunzisoft.keepass.database.cursor.EntryCursorV3; -import com.kunzisoft.keepass.database.cursor.EntryCursorV4; -import com.kunzisoft.keepass.database.exception.*; -import com.kunzisoft.keepass.database.load.ImporterV3; -import com.kunzisoft.keepass.database.load.ImporterV4; -import com.kunzisoft.keepass.database.save.PwDbOutput; -import com.kunzisoft.keepass.database.search.SearchDbHelper; -import com.kunzisoft.keepass.icons.IconDrawableFactory; -import com.kunzisoft.keepass.stream.LEDataInputStream; -import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.UriUtil; -import org.apache.commons.io.FileUtils; - -import javax.annotation.Nullable; -import java.io.*; -import java.util.ArrayList; -import java.util.List; - - -public class Database { - - private static final String TAG = Database.class.getName(); - - private PwDatabase pwDatabase = null; - private PwVersion version = null; - // To keep a reference for specific methods provided by version - private PwDatabaseV3 pwDatabaseV3 = null; - private PwDatabaseV4 pwDatabaseV4 = null; - - private Uri mUri = null; - private SearchDbHelper searchHelper = null; - private boolean readOnly = false; - private boolean passwordEncodingError = false; - - private IconDrawableFactory drawFactory = new IconDrawableFactory(); - - public boolean loaded = false; - - public Database() {} - - public Database(String databasePath) { - // TODO Test with kdb extension - if (isKDBExtension(databasePath)) { - setDatabaseV3(new PwDatabaseV3()); - } else { - PwGroupV4 groupV4 = new PwGroupV4(); - setDatabaseV4(new PwDatabaseV4()); - - groupV4.setTitle(dbNameFromPath(databasePath)); - groupV4.setIconStandard(pwDatabaseV4.getIconFactory().getFolderIcon()); - this.pwDatabaseV4.setRootGroup(groupV4); - } - } - - private void setDatabaseV3(PwDatabaseV3 pwDatabaseV3) { - this.pwDatabaseV3 = pwDatabaseV3; - this.pwDatabaseV4 = null; - this.pwDatabase = pwDatabaseV3; - this.version = pwDatabase.getVersion(); - } - - private void setDatabaseV4(PwDatabaseV4 pwDatabaseV4) { - this.pwDatabaseV3 = null; - this.pwDatabaseV4 = pwDatabaseV4; - this.pwDatabase = pwDatabaseV4; - this.version = pwDatabase.getVersion(); - } - - private boolean isKDBExtension(String filename) { - if (filename == null) { return false; } - int extIdx = filename.lastIndexOf("."); - if (extIdx == -1) return false; - return filename.substring(extIdx).equalsIgnoreCase(".kdb"); - } - - private String dbNameFromPath(String dbPath) { - String filename = URLUtil.guessFileName(dbPath, null, null); - if (EmptyUtils.isNullOrEmpty(filename)) { - return "KeePass Database"; - } - int lastExtDot = filename.lastIndexOf("."); - if (lastExtDot == -1) { - return filename; - } - return filename.substring(0, lastExtDot); - } - - public void setUri(Uri mUri) { - this.mUri = mUri; - } - - public boolean isReadOnly() { - return readOnly; - } - - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - } - - public boolean isPasswordEncodingError() { - return passwordEncodingError; - } - - public IconDrawableFactory getDrawFactory() { - return drawFactory; - } - - public PwIconFactory getIconFactory() { - return pwDatabase.getIconFactory(); - } - - public void loadData(Context ctx, Uri uri, String password, Uri keyfile, ProgressTaskUpdater progressTaskUpdater) - throws IOException, InvalidDBException { - - mUri = uri; - readOnly = false; - if (uri.getScheme().equals("file")) { - File file = new File(uri.getPath()); - readOnly = !file.canWrite(); - } - - // Pass Uris as InputStreams - - InputStream inputStream, keyFileInputStream; - try { - inputStream = UriUtil.getUriInputStream(ctx, uri); - } catch (Exception e) { - Log.e("KPD", "Database::loadData", e); - throw ContentFileNotFoundException.getInstance(uri); - } - - try { - keyFileInputStream = UriUtil.getUriInputStream(ctx, keyfile); - } catch (Exception e) { - Log.e("KPD", "Database::loadData", e); - throw ContentFileNotFoundException.getInstance(keyfile); - } - - // Load Data - - BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream); - if ( ! bufferedInputStream.markSupported() ) { - throw new IOException("Input stream does not support mark."); - } - - // We'll end up reading 8 bytes to identify the header. Might as well use two extra. - bufferedInputStream.mark(10); - - // Get the file directory to save the attachments - int sig1 = LEDataInputStream.readInt(bufferedInputStream); - int sig2 = LEDataInputStream.readInt(bufferedInputStream); - - bufferedInputStream.reset(); // Return to the start - // Header of database V3 - if ( PwDbHeaderV3.matchesHeader(sig1, sig2) ) { - setDatabaseV3(new ImporterV3().openDatabase(bufferedInputStream, - password, - keyFileInputStream, - progressTaskUpdater)); - } - - // Header of database V4 - else if ( PwDbHeaderV4.matchesHeader(sig1, sig2) ) { - setDatabaseV4(new ImporterV4(ctx.getFilesDir()).openDatabase(bufferedInputStream, - password, - keyFileInputStream, - progressTaskUpdater)); - } - - // Header not recognized - else { - throw new InvalidDBSignatureException(); - } - - if ( pwDatabase != null ) { - try { - passwordEncodingError = !pwDatabase.validatePasswordEncoding(password); - searchHelper = new SearchDbHelper(ctx); - loaded = true; - } catch (Exception e) { - Log.e(TAG, "Load can't be performed with this Database version", e); - loaded = false; - } - } - } - - public Boolean isGroupSearchable(GroupVersioned group, Boolean isOmitBackup) { - switch (version) { - case V3: - return pwDatabaseV3.isGroupSearchable(group.getPwGroupV3(), isOmitBackup); - case V4: - return pwDatabaseV4.isGroupSearchable(group.getPwGroupV4(), isOmitBackup); - } - return false; - } - - public GroupVersioned search(String str) { - return search(str, Integer.MAX_VALUE); - } - - public GroupVersioned search(String str, int max) { - if (searchHelper == null) - return null; - return searchHelper.search(this, str, max); - } - - public Cursor searchEntry(String query) { - switch (version) { - case V3: - EntryCursorV3 cursorV3 = new EntryCursorV3(); - if (!query.isEmpty()) { - GroupVersioned searchResult = search(query, 6); - if (searchResult != null) { - for (EntryVersioned entry: searchResult.getChildEntries()) { - if (!entry.isMetaStream()) { // TODO metastream - cursorV3.addEntry(entry.getPwEntryV3()); - } - } - } - } - return cursorV3; - case V4: - EntryCursorV4 cursorv4 = new EntryCursorV4(); - if (!query.isEmpty()) { - GroupVersioned searchResult = search(query, 6); - if (searchResult != null) { - for (EntryVersioned entry: searchResult.getChildEntries()) { - if (!entry.isMetaStream()) { // TODO metastream - cursorv4.addEntry(entry.getPwEntryV4()); - } - } - } - } - return cursorv4; - } - return null; - } - - public EntryVersioned getEntryFrom(Cursor cursor) { - PwIconFactory iconFactory = pwDatabase.getIconFactory(); - EntryVersioned entry = createEntry(); - try { - switch (version) { - case V3: - ((EntryCursorV3) cursor).populateEntry(entry.getPwEntryV3(), iconFactory); - break; - case V4: - // TODO invert field reference manager - startManageEntry(entry); - ((EntryCursorV4) cursor).populateEntry(entry.getPwEntryV4(), iconFactory); - stopManageEntry(entry); - break; - } - } catch (Exception e) { - Log.e(TAG, "This version of PwGroup can't be populated", e); - } - return entry; - } - - public void saveData(Context ctx) throws IOException, PwDbOutputException { - saveData(ctx, mUri); - } - - private void saveData(Context ctx, Uri uri) throws IOException, PwDbOutputException { - String errorMessage = "Failed to store database."; - - if (uri.getScheme().equals("file")) { - String filename = uri.getPath(); - File tempFile = new File(filename + ".tmp"); - - FileOutputStream fos = null; - try { - fos = new FileOutputStream(tempFile); - PwDbOutput pmo = PwDbOutput.getInstance(pwDatabase, fos); - if (pmo != null) - pmo.output(); - } catch (Exception e) { - Log.e(TAG, errorMessage, e); - throw new IOException(errorMessage, e); - } finally { - if (fos != null) - fos.close(); - } - - // Force data to disk before continuing - try { - fos.getFD().sync(); - } catch (SyncFailedException e) { - // Ignore if fsync fails. We tried. - } - - File orig = new File(filename); - - if (!tempFile.renameTo(orig)) { - throw new IOException(errorMessage); - } - } - else { - OutputStream os = null; - try { - os = ctx.getContentResolver().openOutputStream(uri); - PwDbOutput pmo = PwDbOutput.getInstance(pwDatabase, os); - if (pmo != null) - pmo.output(); - } catch (Exception e) { - Log.e(TAG, errorMessage, e); - throw new IOException(errorMessage, e); - } finally { - if (os != null) - os.close(); - } - } - mUri = uri; - } - - // TODO Clear database when lock broadcast is receive in backstage - public void closeAndClear(Context context) { - drawFactory.clearCache(); - // Delete the cache of the database if present - if (pwDatabaseV4 != null) - pwDatabaseV4.clearCache(); - // In all cases, delete all the files in the temp dir - try { - FileUtils.cleanDirectory(context.getFilesDir()); - } catch (IOException e) { - Log.e(TAG, "Unable to clear the directory cache.", e); - } - - pwDatabase = null; - pwDatabaseV4 = null; - mUri = null; - loaded = false; - passwordEncodingError = false; - } - - public String getVersion() { - return version.toString(); - } - - public boolean containsName() { - switch (version) { - default: - return false; - case V4: - return true; - } - } - - public String getName() { - switch (version) { - default: - return ""; - case V4: - return pwDatabaseV4.getName(); - } - } - - public void assignName(String name) { - switch (version) { - case V4: - pwDatabaseV4.setName(name); - pwDatabaseV4.setNameChanged(new PwDate()); - break; - } - } - - public boolean containsDescription() { - switch (version) { - default: - return false; - case V4: - return true; - } - } - - public String getDescription() { - switch (version) { - default: - return ""; - case V4: - return pwDatabaseV4.getDescription(); - } - } - - public void assignDescription(String description) { - switch (version) { - case V4: - pwDatabaseV4.setDescription(description); - pwDatabaseV4.setDescriptionChanged(new PwDate()); - } - } - - public String getDefaultUsername() { - switch (version) { - default: - return ""; - case V4: - return pwDatabaseV4.getDefaultUserName(); - } - } - - public void setDefaultUsername(String username) { - switch (version) { - case V4: - pwDatabaseV4.setDefaultUserName(username); - pwDatabaseV4.setDefaultUserNameChanged(new PwDate()); - } - } - - public PwEncryptionAlgorithm getEncryptionAlgorithm() { - return pwDatabase.getEncryptionAlgorithm(); - } - - public List getAvailableEncryptionAlgorithms() { - return pwDatabase.getAvailableEncryptionAlgorithms(); - } - - public boolean allowEncryptionAlgorithmModification() { - return getAvailableEncryptionAlgorithms().size() > 1; - } - - public void assignEncryptionAlgorithm(PwEncryptionAlgorithm algorithm) { - switch (version) { - case V4: - pwDatabaseV4.setEncryptionAlgorithm(algorithm); - pwDatabaseV4.setDataEngine(algorithm.getCipherEngine()); - pwDatabaseV4.setDataCipher(algorithm.getDataCipher()); - } - } - - public String getEncryptionAlgorithmName(Resources resources) { - return pwDatabase.getEncryptionAlgorithm().getName(resources); - } - - public List getAvailableKdfEngines() { - switch (version) { - case V4: - return KdfFactory.kdfListV4; - case V3: - return KdfFactory.kdfListV3; - } - return new ArrayList<>(); - } - - public boolean allowKdfModification() { - return getAvailableKdfEngines().size() > 1; - } - - public KdfEngine getKdfEngine() { - switch (version) { - case V4: - KdfEngine kdfEngine = pwDatabaseV4.getKdfEngine(); - if (kdfEngine == null) - return KdfFactory.aesKdf; - return kdfEngine; - default: - case V3: - return KdfFactory.aesKdf; - } - } - - public void assignKdfEngine(KdfEngine kdfEngine) { - switch (version) { - case V4: - if (pwDatabaseV4.getKdfParameters() == null - || !pwDatabaseV4.getKdfParameters().getUUID().equals(kdfEngine.getDefaultParameters().getUUID())) - pwDatabaseV4.setKdfParameters(kdfEngine.getDefaultParameters()); - setNumberKeyEncryptionRounds(kdfEngine.getDefaultKeyRounds()); - setMemoryUsage(kdfEngine.getDefaultMemoryUsage()); - setParallelism(kdfEngine.getDefaultParallelism()); - break; - } - } - - public String getKeyDerivationName(Resources resources) { - KdfEngine kdfEngine = getKdfEngine(); - if (kdfEngine != null) { - return kdfEngine.getName(resources); - } - return ""; - } - - public String getNumberKeyEncryptionRoundsAsString() { - return Long.toString(getNumberKeyEncryptionRounds()); - } - - public long getNumberKeyEncryptionRounds() { - return pwDatabase.getNumberKeyEncryptionRounds(); - } - - public void setNumberKeyEncryptionRounds(long numberRounds) throws NumberFormatException { - pwDatabase.setNumberKeyEncryptionRounds(numberRounds); - } - - public String getMemoryUsageAsString() { - return Long.toString(getMemoryUsage()); - } - - public long getMemoryUsage() { - switch (version) { - case V4: - return pwDatabaseV4.getMemoryUsage(); - } - return KdfEngine.UNKNOW_VALUE; - } - - public void setMemoryUsage(long memory) { - switch (version) { - case V4: - pwDatabaseV4.setMemoryUsage(memory); - } - } - - public String getParallelismAsString() { - return Integer.toString(getParallelism()); - } - - public int getParallelism() { - switch (version) { - case V4: - return pwDatabaseV4.getParallelism(); - } - return KdfEngine.UNKNOW_VALUE; - } - - public void setParallelism(int parallelism) { - switch (version) { - case V4: - pwDatabaseV4.setParallelism(parallelism); - } - } - - public boolean validatePasswordEncoding(String key) { - return pwDatabase.validatePasswordEncoding(key); - } - - public byte[] getMasterKey() { - return pwDatabase.getMasterKey(); - } - - public void setMasterKey(byte[] masterKey) { - pwDatabase.masterKey = masterKey; - } - - public void retrieveMasterKey(String key, InputStream keyInputStream) - throws InvalidKeyFileException, IOException { - pwDatabase.retrieveMasterKey(key, keyInputStream); - } - - public GroupVersioned getRootGroup() { - switch (version) { - case V3: - return new GroupVersioned(pwDatabaseV3.getRootGroup()); - case V4: - return new GroupVersioned(pwDatabaseV4.getRootGroup()); - } - return null; - } - - public EntryVersioned createEntry() { - EntryVersioned newEntry = null; - try { - switch (version) { - case V3: - newEntry = new EntryVersioned(new PwEntryV3()); - newEntry.setNodeId(pwDatabaseV3.newEntryId()); - - case V4: - newEntry = new EntryVersioned(new PwEntryV4()); - newEntry.setNodeId(pwDatabaseV4.newEntryId()); - } - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be created", e); - } - return newEntry; - } - - public GroupVersioned createGroup() { - GroupVersioned newPwGroup = null; - try { - switch (version) { - case V3: - newPwGroup = new GroupVersioned(new PwGroupV3()); - newPwGroup.setNodeId(pwDatabaseV3.newGroupId()); - - case V4: - newPwGroup = new GroupVersioned(new PwGroupV4()); - newPwGroup.setNodeId(pwDatabaseV4.newGroupId()); - } - } catch (Exception e) { - Log.e(TAG, "This version of PwGroup can't be created", e); - } - return newPwGroup; - } - - public EntryVersioned getEntryById(PwNodeId id) { - PwEntryV3 entryV3 = pwDatabaseV3.getEntryById(id); - if (entryV3 != null) - return new EntryVersioned(entryV3); - - PwEntryV4 entryV4 = pwDatabaseV4.getEntryById(id); - if (entryV4 != null) - return new EntryVersioned(entryV4); - - return null; - } - - public GroupVersioned getGroupById(PwNodeId id) { - if (pwDatabaseV3 != null) { - PwGroupV3 groupV3 = pwDatabaseV3.getGroupById(id); - if (groupV3 != null) - return new GroupVersioned(groupV3); - } - - if (pwDatabaseV4 != null) { - PwGroupV4 groupV4 = pwDatabaseV4.getGroupById(id); - if (groupV4 != null) - return new GroupVersioned(groupV4); - } - - return null; - } - - public void addEntryTo(EntryVersioned entry, GroupVersioned parent) { - switch (version) { - case V3: - pwDatabaseV3.addEntryTo(entry.getPwEntryV3(), parent.getPwGroupV3()); - case V4: - pwDatabaseV4.addEntryTo(entry.getPwEntryV4(), parent.getPwGroupV4()); - } - } - - public void removeEntryFrom(EntryVersioned entry, GroupVersioned parent) { - switch (version) { - case V3: - pwDatabaseV3.removeEntryFrom(entry.getPwEntryV3(), parent.getPwGroupV3()); - case V4: - pwDatabaseV4.removeEntryFrom(entry.getPwEntryV4(), parent.getPwGroupV4()); - } - } - - public void addGroupTo(GroupVersioned group, GroupVersioned parent) { - switch (version) { - case V3: - pwDatabaseV3.addGroupTo(group.getPwGroupV3(), parent.getPwGroupV3()); - case V4: - pwDatabaseV4.addGroupTo(group.getPwGroupV4(), parent.getPwGroupV4()); - } - } - - public void removeGroupFrom(GroupVersioned group, GroupVersioned parent) { - switch (version) { - case V3: - pwDatabaseV3.removeGroupFrom(group.getPwGroupV3(), parent.getPwGroupV3()); - case V4: - pwDatabaseV4.removeGroupFrom(group.getPwGroupV4(), parent.getPwGroupV4()); - } - } - - /** - * @return A duplicate entry with the same values, a new UUID, - * @param entryToCopy - * @param newParent - */ - public @Nullable EntryVersioned copyEntry(EntryVersioned entryToCopy, GroupVersioned newParent) { - try { - EntryVersioned entryCopied = null; - switch (version) { - case V3: - entryCopied = new EntryVersioned(entryToCopy); - break; - case V4: - entryCopied = new EntryVersioned(entryToCopy); - break; - } - entryCopied.setNodeId(new PwNodeIdUUID()); - entryCopied.setParent(newParent); - addEntryTo(entryCopied, newParent); - return entryCopied; - } catch (Exception e) { - Log.e(TAG, "This version of PwEntry can't be updated", e); - } - return null; - } - - public void moveEntry(EntryVersioned entryToMove, GroupVersioned newParent) { - removeEntryFrom(entryToMove, entryToMove.getParent()); - addEntryTo(entryToMove, newParent); - } - - public void moveGroup(GroupVersioned groupToMove, GroupVersioned newParent) { - removeGroupFrom(groupToMove, groupToMove.getParent()); - addGroupTo(groupToMove, newParent); - } - - public void deleteEntry(EntryVersioned entry) { - removeEntryFrom(entry, entry.getParent()); - } - - public void deleteGroup(GroupVersioned group) { - group.doForEachChildAndForIt( - new NodeHandler() { - @Override - public boolean operate(EntryVersioned entry) { - deleteEntry(entry); - return true; - } - }, - new NodeHandler() { - @Override - public boolean operate(GroupVersioned group) { - GroupVersioned parent = group.getParent(); - removeGroupFrom(group, parent); - return true; - } - }); - } - - public void undoDeleteEntry(EntryVersioned entry, GroupVersioned parent) { - switch (version) { - case V3: - pwDatabaseV3.undoDeleteEntryFrom(entry.getPwEntryV3(), parent.getPwGroupV3()); - case V4: - pwDatabaseV4.undoDeleteEntryFrom(entry.getPwEntryV4(), parent.getPwGroupV4()); - } - } - - public void undoDeleteGroup(GroupVersioned group, GroupVersioned parent) { - switch (version) { - case V3: - pwDatabaseV3.undoDeleteGroupFrom(group.getPwGroupV3(), parent.getPwGroupV3()); - case V4: - pwDatabaseV4.undoDeleteGroupFrom(group.getPwGroupV4(), parent.getPwGroupV4()); - } - } - - /** - * Determine if RecycleBin is available or not for this version of database - * @return true if RecycleBin available - */ - public boolean isRecycleBinAvailable() { - return pwDatabaseV4 != null; - } - - public boolean isRecycleBinEnabled() { - if (pwDatabaseV4 != null) { - return pwDatabaseV4.isRecycleBinEnabled(); - } - return false; - } - - public GroupVersioned getRecycleBin() { - if (pwDatabaseV4 != null) { - return new GroupVersioned(pwDatabaseV4.getRecycleBin()); - } - return null; - } - - public boolean canRecycle(EntryVersioned entry) { - if (pwDatabaseV4 != null) { - return pwDatabaseV4.canRecycle(entry.getPwEntryV4()); - } - return false; - } - - public boolean canRecycle(GroupVersioned group) { - if (pwDatabaseV4 != null) { - return pwDatabaseV4.canRecycle(group.getPwGroupV4()); - } - return false; - } - - public void recycle(EntryVersioned entry) { - if (pwDatabaseV4 != null) { - pwDatabaseV4.recycle(entry.getPwEntryV4()); - } - } - - public void recycle(GroupVersioned group) { - if (pwDatabaseV4 != null) { - pwDatabaseV4.recycle(group.getPwGroupV4()); - } - } - - public void undoRecycle(EntryVersioned entry, GroupVersioned parent) { - if (pwDatabaseV4 != null) { - pwDatabaseV4.undoRecycle(entry.getPwEntryV4(), parent.getPwGroupV4()); - } - } - - public void undoRecycle(GroupVersioned group, GroupVersioned parent) { - if (pwDatabaseV4 != null) { - pwDatabaseV4.undoRecycle(group.getPwGroupV4(), parent.getPwGroupV4()); - } - } - - public void startManageEntry(EntryVersioned entry) { - if (pwDatabaseV4 != null) { - entry.startToManageFieldReferences(pwDatabaseV4); - } - } - - public void stopManageEntry(EntryVersioned entry) { - if (pwDatabaseV4 != null) { - entry.stopToManageFieldReferences(); - } - } - - public void createBackupOf(EntryVersioned entry) { - if (pwDatabaseV4 != null) { - entry.createBackup(pwDatabaseV4); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt new file mode 100644 index 000000000..bf349fc54 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -0,0 +1,702 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.content.Context +import android.content.res.Resources +import android.database.Cursor +import android.net.Uri +import android.util.Log +import android.webkit.URLUtil +import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine +import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory +import com.kunzisoft.keepass.database.NodeHandler +import com.kunzisoft.keepass.database.cursor.EntryCursorV3 +import com.kunzisoft.keepass.database.cursor.EntryCursorV4 +import com.kunzisoft.keepass.database.exception.* +import com.kunzisoft.keepass.database.load.ImporterV3 +import com.kunzisoft.keepass.database.load.ImporterV4 +import com.kunzisoft.keepass.database.save.PwDbV3Output +import com.kunzisoft.keepass.database.save.PwDbV4Output +import com.kunzisoft.keepass.database.search.SearchDbHelper +import com.kunzisoft.keepass.icons.IconDrawableFactory +import com.kunzisoft.keepass.stream.LEDataInputStream +import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import com.kunzisoft.keepass.utils.EmptyUtils +import com.kunzisoft.keepass.utils.UriUtil +import org.apache.commons.io.FileUtils +import java.io.* +import java.util.* + + +class Database { + + // To keep a reference for specific methods provided by version + private var pwDatabaseV3: PwDatabaseV3? = null + private var pwDatabaseV4: PwDatabaseV4? = null + + private var mUri: Uri? = null + private var searchHelper: SearchDbHelper? = null + var isReadOnly = false + var isPasswordEncodingError = false + private set + + val drawFactory = IconDrawableFactory() + + var loaded = false + + val iconFactory: PwIconFactory? + get() { + return pwDatabaseV3?.getIconFactory() ?: pwDatabaseV4?.getIconFactory() + } + + val name: String + get() { + return pwDatabaseV4?.name ?: "" + } + + val description: String + get() { + return pwDatabaseV4?.description ?: "" + } + + var defaultUsername: String + get() { + return pwDatabaseV4?.defaultUserName ?: "" + } + set(username) { + pwDatabaseV4?.defaultUserName = username + pwDatabaseV4?.defaultUserNameChanged = PwDate() + } + + val encryptionAlgorithm: PwEncryptionAlgorithm? + get() { + return pwDatabaseV4?.encryptionAlgorithm + } + + val availableEncryptionAlgorithms: List + get() = pwDatabaseV3?.availableEncryptionAlgorithms ?: pwDatabaseV4?.availableEncryptionAlgorithms ?: ArrayList() + + val availableKdfEngines: List + get() { + if (pwDatabaseV3 != null) { + return KdfFactory.kdfListV3 + } + if (pwDatabaseV4 != null) { + return KdfFactory.kdfListV4 + } + return ArrayList() + } + + val kdfEngine: KdfEngine? + get() { + return pwDatabaseV4?.kdfEngine ?: return KdfFactory.aesKdf + } + + val numberKeyEncryptionRoundsAsString: String + get() = java.lang.Long.toString(numberKeyEncryptionRounds) + + var numberKeyEncryptionRounds: Long + get() = pwDatabaseV3?.numberKeyEncryptionRounds ?: pwDatabaseV4?.numberKeyEncryptionRounds ?: 0 + @Throws(NumberFormatException::class) + set(numberRounds) { + pwDatabaseV3?.numberKeyEncryptionRounds = numberRounds + pwDatabaseV4?.numberKeyEncryptionRounds = numberRounds + } + + val memoryUsageAsString: String + get() = java.lang.Long.toString(memoryUsage) + + var memoryUsage: Long + get() { + return pwDatabaseV4?.memoryUsage ?: return KdfEngine.UNKNOW_VALUE.toLong() + } + set(memory) { + pwDatabaseV4?.memoryUsage = memory + } + + val parallelismAsString: String + get() = Integer.toString(parallelism) + + var parallelism: Int + get() = pwDatabaseV4?.parallelism ?: KdfEngine.UNKNOW_VALUE + set(parallelism) { + pwDatabaseV4?.parallelism = parallelism + } + + var masterKey: ByteArray + get() = pwDatabaseV3?.getMasterKey() ?: pwDatabaseV4?.getMasterKey() ?: ByteArray(32) + set(masterKey) { + pwDatabaseV3?.masterKey = masterKey + pwDatabaseV4?.masterKey = masterKey + } + + val rootGroup: GroupVersioned? + get() { + pwDatabaseV3?.let { + return GroupVersioned(it.rootGroup) + } + pwDatabaseV4?.let { + return GroupVersioned(it.rootGroup) + } + return null + } + + /** + * Determine if RecycleBin is available or not for this version of database + * @return true if RecycleBin available + */ + val isRecycleBinAvailable: Boolean + get() = pwDatabaseV4 != null + + val isRecycleBinEnabled: Boolean + get() = if (pwDatabaseV4 != null) { + pwDatabaseV4!!.isRecycleBinEnabled + } else false + + val recycleBin: GroupVersioned? + get() = if (pwDatabaseV4 != null) { + GroupVersioned(pwDatabaseV4!!.recycleBin!!) + } else null + + constructor() {} + + constructor(databasePath: String) { + // TODO Test with kdb extension + if (isKDBExtension(databasePath)) { + setDatabaseV3(PwDatabaseV3()) + } else { + val groupV4 = PwGroupV4() + setDatabaseV4(PwDatabaseV4()) + + groupV4.title = dbNameFromPath(databasePath) + groupV4.setIconStandard(pwDatabaseV4!!.getIconFactory().folderIcon) + this.pwDatabaseV4!!.rootGroup = groupV4 + } + } + + private fun setDatabaseV3(pwDatabaseV3: PwDatabaseV3) { + this.pwDatabaseV3 = pwDatabaseV3 + this.pwDatabaseV4 = null + } + + private fun setDatabaseV4(pwDatabaseV4: PwDatabaseV4) { + this.pwDatabaseV3 = null + this.pwDatabaseV4 = pwDatabaseV4 + } + + private fun isKDBExtension(filename: String?): Boolean { + if (filename == null) { + return false + } + val extIdx = filename.lastIndexOf(".") + return if (extIdx == -1) false else filename.substring(extIdx).equals(".kdb", ignoreCase = true) + } + + private fun dbNameFromPath(dbPath: String): String { + val filename = URLUtil.guessFileName(dbPath, null, null) + if (EmptyUtils.isNullOrEmpty(filename)) { + return "KeePass Database" + } + val lastExtDot = filename.lastIndexOf(".") + return if (lastExtDot == -1) { + filename + } else filename.substring(0, lastExtDot) + } + + fun setUri(mUri: Uri) { + this.mUri = mUri + } + + @Throws(IOException::class, InvalidDBException::class) + fun loadData(ctx: Context, uri: Uri, password: String?, keyfile: Uri?, progressTaskUpdater: ProgressTaskUpdater) { + + mUri = uri + isReadOnly = false + if (uri.scheme == "file") { + val file = File(uri.path!!) + isReadOnly = !file.canWrite() + } + + // Pass Uris as InputStreams + val inputStream: InputStream + try { + inputStream = UriUtil.getUriInputStream(ctx, uri) + } catch (e: Exception) { + Log.e("KPD", "Database::loadData", e) + throw ContentFileNotFoundException.getInstance(uri) + } + + // Pass KeyFile Uri as InputStreams + var keyFileInputStream: InputStream? = null + keyfile?.let { + try { + keyFileInputStream = UriUtil.getUriInputStream(ctx, keyfile) + } catch (e: Exception) { + Log.e("KPD", "Database::loadData", e) + throw ContentFileNotFoundException.getInstance(keyfile) + } + } + + // Load Data + + val bufferedInputStream = BufferedInputStream(inputStream) + if (!bufferedInputStream.markSupported()) { + throw IOException("Input stream does not support mark.") + } + + // We'll end up reading 8 bytes to identify the header. Might as well use two extra. + bufferedInputStream.mark(10) + + // Get the file directory to save the attachments + val sig1 = LEDataInputStream.readInt(bufferedInputStream) + val sig2 = LEDataInputStream.readInt(bufferedInputStream) + + // Return to the start + bufferedInputStream.reset() + when { + // Header of database V3 + PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3().openDatabase(bufferedInputStream, + password, + keyFileInputStream, + progressTaskUpdater)) + + // Header of database V4 + PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(ctx.filesDir).openDatabase(bufferedInputStream, + password, + keyFileInputStream, + progressTaskUpdater)) + + // Header not recognized + else -> throw InvalidDBSignatureException() + } + + try { + isPasswordEncodingError = !(pwDatabaseV3?.validatePasswordEncoding(password) ?: pwDatabaseV4?.validatePasswordEncoding(password) ?: true) + searchHelper = SearchDbHelper(ctx) + loaded = true + } catch (e: Exception) { + Log.e(TAG, "Load can't be performed with this Database version", e) + loaded = false + } + } + + fun isGroupSearchable(group: GroupVersioned, isOmitBackup: Boolean): Boolean { + return pwDatabaseV3?.isGroupSearchable(group.pwGroupV3, isOmitBackup) ?: + pwDatabaseV4?.isGroupSearchable(group.pwGroupV4, isOmitBackup) ?: + false + } + + @JvmOverloads + fun search(str: String, max: Int = Integer.MAX_VALUE): GroupVersioned? { + return if (searchHelper == null) null else searchHelper?.search(this, str, max) + } + + fun searchEntry(query: String): Cursor? { + if (pwDatabaseV3 != null) { + val cursorV3 = EntryCursorV3() + if (query.isNotEmpty()) { + val searchResult = search(query, 6) + if (searchResult != null) { + for (entry in searchResult.getChildEntries()) { + if (!entry.isMetaStream) { // TODO metastream + cursorV3.addEntry(entry.pwEntryV3) + } + } + } + } + return cursorV3 + } + + if (pwDatabaseV4 != null) { + val cursorV4 = EntryCursorV4() + if (query.isNotEmpty()) { + val searchResult = search(query, 6) + if (searchResult != null) { + for (entry in searchResult.getChildEntries()) { + if (!entry.isMetaStream) { // TODO metastream + cursorV4.addEntry(entry.pwEntryV4) + } + } + } + } + return cursorV4 + } + return null + } + + fun getEntryFrom(cursor: Cursor): EntryVersioned? { + val iconFactory = pwDatabaseV3?.getIconFactory() ?: pwDatabaseV4?.getIconFactory() + val entry = createEntry() + + // TODO invert field reference manager + entry?.let { entryVersioned -> + startManageEntry(entryVersioned) + pwDatabaseV3?.let { (cursor as EntryCursorV3).populateEntry(entryVersioned.pwEntryV3, iconFactory) } + pwDatabaseV4?.let { (cursor as EntryCursorV4).populateEntry(entryVersioned.pwEntryV4, iconFactory) } + stopManageEntry(entryVersioned) + } + + return entry + } + + @Throws(IOException::class, PwDbOutputException::class) + fun saveData(ctx: Context) { + saveData(ctx, mUri!!) + } + + @Throws(IOException::class, PwDbOutputException::class) + private fun saveData(ctx: Context, uri: Uri) { + val errorMessage = "Failed to store database." + + if (uri.scheme == "file") { + val filename = uri.path + val tempFile = File(filename!! + ".tmp") + + var fileOutputStream: FileOutputStream? = null + try { + fileOutputStream = FileOutputStream(tempFile) + val pmo = + pwDatabaseV3?.let { PwDbV3Output(it, fileOutputStream) } + ?: pwDatabaseV4?.let { PwDbV4Output(it, fileOutputStream) } + pmo?.output() + } catch (e: Exception) { + Log.e(TAG, errorMessage, e) + throw IOException(errorMessage, e) + } finally { + fileOutputStream?.close() + } + + // Force data to disk before continuing + try { + fileOutputStream!!.fd.sync() + } catch (e: SyncFailedException) { + // Ignore if fsync fails. We tried. + } + + val orig = File(filename) + + if (!tempFile.renameTo(orig)) { + throw IOException(errorMessage) + } + } else { + var outputStream: OutputStream? = null + try { + outputStream = ctx.contentResolver.openOutputStream(uri) + val pmo = + pwDatabaseV3?.let { PwDbV3Output(it, outputStream) } + ?: pwDatabaseV4?.let { PwDbV4Output(it, outputStream) } + pmo?.output() + } catch (e: Exception) { + Log.e(TAG, errorMessage, e) + throw IOException(errorMessage, e) + } finally { + outputStream?.close() + } + } + mUri = uri + } + + // TODO Clear database when lock broadcast is receive in backstage + fun closeAndClear(context: Context) { + drawFactory.clearCache() + // Delete the cache of the database if present + if (pwDatabaseV4 != null) + pwDatabaseV4!!.clearCache() + // In all cases, delete all the files in the temp dir + try { + FileUtils.cleanDirectory(context.filesDir) + } catch (e: IOException) { + Log.e(TAG, "Unable to clear the directory cache.", e) + } + + pwDatabaseV4 = null + mUri = null + loaded = false + isPasswordEncodingError = false + } + + fun getVersion(): String { + return pwDatabaseV3?.version?.toString() ?: pwDatabaseV4?.version?.toString() ?: PwVersion.UNKNOWN.toString() + } + + fun containsName(): Boolean { + pwDatabaseV4?.let { return true } + return false + } + + fun assignName(name: String) { + pwDatabaseV4?.name = name + pwDatabaseV4?.nameChanged = PwDate() + } + + fun containsDescription(): Boolean { + pwDatabaseV4?.let { return true } + return false + } + + fun assignDescription(description: String) { + pwDatabaseV4?.description = description + pwDatabaseV4?.descriptionChanged = PwDate() + } + + fun allowEncryptionAlgorithmModification(): Boolean { + return availableEncryptionAlgorithms.size > 1 + } + + fun assignEncryptionAlgorithm(algorithm: PwEncryptionAlgorithm) { + pwDatabaseV4?.encryptionAlgorithm = algorithm + pwDatabaseV4?.setDataEngine(algorithm.cipherEngine) + pwDatabaseV4?.dataCipher = algorithm.dataCipher + } + + fun getEncryptionAlgorithmName(resources: Resources): String { + return pwDatabaseV3?.encryptionAlgorithm?.getName(resources) ?: pwDatabaseV4?.encryptionAlgorithm?.getName(resources) ?: "" + } + + fun allowKdfModification(): Boolean { + return availableKdfEngines.size > 1 + } + + fun assignKdfEngine(kdfEngine: KdfEngine) { + if (pwDatabaseV4?.kdfParameters?.uuid != kdfEngine.defaultParameters.uuid) + pwDatabaseV4?.kdfParameters = kdfEngine.defaultParameters + numberKeyEncryptionRounds = kdfEngine.defaultKeyRounds + memoryUsage = kdfEngine.defaultMemoryUsage + parallelism = kdfEngine.defaultParallelism + } + + fun getKeyDerivationName(resources: Resources): String { + val kdfEngine = kdfEngine + return if (kdfEngine != null) { + kdfEngine.getName(resources) + } else "" + } + + fun validatePasswordEncoding(key: String): Boolean { + return pwDatabaseV3?.validatePasswordEncoding(key) ?: pwDatabaseV4?.validatePasswordEncoding(key) ?: false + } + + @Throws(InvalidKeyFileException::class, IOException::class) + fun retrieveMasterKey(key: String, keyInputStream: InputStream) { + pwDatabaseV3?.retrieveMasterKey(key, keyInputStream) + pwDatabaseV4?.retrieveMasterKey(key, keyInputStream) + } + + fun createEntry(): EntryVersioned? { + + pwDatabaseV3?.let { database -> + return EntryVersioned(PwEntryV3()).apply { + database.newEntryId()?.let { + nodeId = it + } + } + } + + + pwDatabaseV4?.let { database -> + return EntryVersioned(PwEntryV4()).apply { + database.newEntryId()?.let { + nodeId = it + } + } + } + + return null + } + + fun createGroup(): GroupVersioned? { + + pwDatabaseV3?.let { database -> + return GroupVersioned(PwGroupV3()).apply { + database.newGroupId()?.let { + setNodeId(it) + } + } + } + + + pwDatabaseV4?.let { database -> + return GroupVersioned(PwGroupV4()).apply { + database.newGroupId()?.let { + setNodeId(it) + } + } + } + + return null + } + + fun getEntryById(id: PwNodeId<*>): EntryVersioned? { + pwDatabaseV3?.getEntryById(id)?.let { + return EntryVersioned(it) + } + pwDatabaseV4?.getEntryById(id)?.let { + return EntryVersioned(it) + } + return null + } + + fun getGroupById(id: PwNodeId<*>): GroupVersioned? { + pwDatabaseV3?.getGroupById(id)?.let { + return GroupVersioned(it) + } + + pwDatabaseV4?.getGroupById(id)?.let { + return GroupVersioned(it) + } + + return null + } + + fun addEntryTo(entry: EntryVersioned, parent: GroupVersioned) { + pwDatabaseV3?.addEntryTo(entry.pwEntryV3, parent.pwGroupV3) + pwDatabaseV4?.addEntryTo(entry.pwEntryV4, parent.pwGroupV4) + } + + fun removeEntryFrom(entry: EntryVersioned, parent: GroupVersioned) { + pwDatabaseV3?.removeEntryFrom(entry.pwEntryV3, parent.pwGroupV3) + pwDatabaseV4?.removeEntryFrom(entry.pwEntryV4, parent.pwGroupV4) + } + + fun addGroupTo(group: GroupVersioned, parent: GroupVersioned) { + pwDatabaseV3?.addGroupTo(group.pwGroupV3, parent.pwGroupV3) + pwDatabaseV4?.addGroupTo(group.pwGroupV4, parent.pwGroupV4) + } + + fun removeGroupFrom(group: GroupVersioned, parent: GroupVersioned) { + pwDatabaseV3?.removeGroupFrom(group.pwGroupV3, parent.pwGroupV3) + pwDatabaseV4?.removeGroupFrom(group.pwGroupV4, parent.pwGroupV4) + } + + /** + * @return A duplicate entry with the same values, a new UUID, + * @param entryToCopy + * @param newParent + */ + fun copyEntry(entryToCopy: EntryVersioned, newParent: GroupVersioned): EntryVersioned? { + try { + val entryCopied = EntryVersioned(entryToCopy) + entryCopied.nodeId = PwNodeIdUUID() + entryCopied.parent = newParent + addEntryTo(entryCopied, newParent) + return entryCopied + } catch (e: Exception) { + Log.e(TAG, "This version of PwEntry can't be updated", e) + } + + return null + } + + fun moveEntry(entryToMove: EntryVersioned, newParent: GroupVersioned) { + entryToMove.parent?.let { + removeEntryFrom(entryToMove, it) + } + addEntryTo(entryToMove, newParent) + } + + fun moveGroup(groupToMove: GroupVersioned, newParent: GroupVersioned) { + groupToMove.parent?.let { + removeGroupFrom(groupToMove, it) + } + addGroupTo(groupToMove, newParent) + } + + fun deleteEntry(entry: EntryVersioned) { + entry.parent?.let { + removeEntryFrom(entry, it) + } + } + + fun deleteGroup(group: GroupVersioned) { + group.doForEachChildAndForIt( + object : NodeHandler() { + override fun operate(entry: EntryVersioned): Boolean { + deleteEntry(entry) + return true + } + }, + object : NodeHandler() { + override fun operate(group: GroupVersioned): Boolean { + group.parent?.let { + removeGroupFrom(group, it) + } + return true + } + }) + } + + fun undoDeleteEntry(entry: EntryVersioned, parent: GroupVersioned) { + pwDatabaseV3?.undoDeleteEntryFrom(entry.pwEntryV3, parent.pwGroupV3) + pwDatabaseV4?.undoDeleteEntryFrom(entry.pwEntryV4, parent.pwGroupV4) + } + + fun undoDeleteGroup(group: GroupVersioned, parent: GroupVersioned) { + pwDatabaseV3?.undoDeleteGroupFrom(group.pwGroupV3, parent.pwGroupV3) + pwDatabaseV4?.undoDeleteGroupFrom(group.pwGroupV4, parent.pwGroupV4) + } + + fun canRecycle(entry: EntryVersioned): Boolean { + return pwDatabaseV4?.canRecycle(entry.pwEntryV4) ?: false + } + + fun canRecycle(group: GroupVersioned): Boolean { + return pwDatabaseV4?.canRecycle(group.pwGroupV4) ?: false + } + + fun recycle(entry: EntryVersioned) { + pwDatabaseV4?.recycle(entry.pwEntryV4) + } + + fun recycle(group: GroupVersioned) { + pwDatabaseV4?.recycle(group.pwGroupV4) + } + + fun undoRecycle(entry: EntryVersioned, parent: GroupVersioned) { + pwDatabaseV4?.undoRecycle(entry.pwEntryV4, parent.pwGroupV4) + } + + fun undoRecycle(group: GroupVersioned, parent: GroupVersioned) { + pwDatabaseV4?.undoRecycle(group.pwGroupV4, parent.pwGroupV4) + } + + fun startManageEntry(entry: EntryVersioned) { + pwDatabaseV4?.let { + entry.startToManageFieldReferences(it) + } + } + + fun stopManageEntry(entry: EntryVersioned) { + pwDatabaseV4?.let { + entry.stopToManageFieldReferences() + } + } + + fun createBackupOf(entry: EntryVersioned) { + pwDatabaseV4?.let { + entry.createBackup(it) + } + } + + companion object { + + private val TAG = Database::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index fdf31e880..5a88c944b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -60,7 +60,7 @@ public class PwDatabaseV4 extends PwDatabase { private long numKeyEncRounds; private VariantDictionary publicCustomData = new VariantDictionary(); - protected String name = "KeePass DX database"; + private String name = "KeePass DX database"; private PwDate nameChanged = new PwDate(); private PwDate settingsChanged = new PwDate(); private String description = ""; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java index dd4e9f1b5..756c8f2c1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java @@ -20,7 +20,7 @@ package com.kunzisoft.keepass.database.element; public enum PwVersion { - V3, V4; + UNKNOWN, V3, V4; @Override public String toString() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index e8072b2b1..b8ad4b184 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -77,7 +77,10 @@ public class ImporterV3 extends Importer { private PwDatabaseV3 databaseToOpen; @Override - public PwDatabaseV3 openDatabase(InputStream inStream, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater) + public PwDatabaseV3 openDatabase(InputStream inStream, + String password, + InputStream kfIs, + ProgressTaskUpdater progressTaskUpdater) throws IOException, InvalidDBException { // Load entire file, most of it's encrypted. diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 6c1baebe7..43245cfc8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -73,8 +73,10 @@ public class ImporterV4 extends Importer { } @Override - public PwDatabaseV4 openDatabase(InputStream inStream, String password, - InputStream keyInputStream, ProgressTaskUpdater progressTaskUpdater) + public PwDatabaseV4 openDatabase(InputStream inStream, + String password, + InputStream keyInputStream, + ProgressTaskUpdater progressTaskUpdater) throws IOException, InvalidDBException { if (progressTaskUpdater != null) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java index cdc4769d9..2cbcca5d8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java @@ -19,9 +19,6 @@ */ package com.kunzisoft.keepass.database.save; -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwDatabaseV3; -import com.kunzisoft.keepass.database.element.PwDatabaseV4; import com.kunzisoft.keepass.database.element.PwDbHeader; import com.kunzisoft.keepass.database.exception.PwDbOutputException; @@ -33,16 +30,6 @@ public abstract class PwDbOutput
{ protected OutputStream mOS; - public static PwDbOutput getInstance(PwDatabase pm, OutputStream os) { - if ( pm instanceof PwDatabaseV3) { - return new PwDbV3Output((PwDatabaseV3)pm, os); - } else if ( pm instanceof PwDatabaseV4) { - return new PwDbV4Output((PwDatabaseV4)pm, os); - } - - return null; - } - protected PwDbOutput(OutputStream os) { mOS = os; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index dcec07d1d..ea4ec5cc5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -65,7 +65,7 @@ public class PwDbV4Output extends PwDbOutput { private byte[] headerHmac; private CipherEngine engine = null; - protected PwDbV4Output(PwDatabaseV4 pm, OutputStream os) { + public PwDbV4Output(PwDatabaseV4 pm, OutputStream os) { super(os); this.mPM = pm; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt index 70c0d594e..1b1fd9204 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt @@ -49,7 +49,7 @@ class SearchDbHelper(private val mContext: Context) { incrementEntry = 0 - pm.rootGroup.doForEachChild( + pm.rootGroup?.doForEachChild( object : NodeHandler() { override fun operate(entry: EntryVersioned): Boolean { if (entryContainsString(entry, finalQStr, loc)) { diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index eb7d44271..dd66947db 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -45,13 +45,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - +import android.widget.*; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; @@ -63,10 +57,9 @@ import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.compat.ClipDataCompat; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; +import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; import com.kunzisoft.keepass.fileselect.KeyFileHelper; import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; @@ -75,22 +68,16 @@ import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; +import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.UriUtil; - import org.jetbrains.annotations.Nullable; +import permissions.dispatcher.*; import java.io.File; import java.io.FileNotFoundException; -import permissions.dispatcher.NeedsPermission; -import permissions.dispatcher.OnNeverAskAgain; -import permissions.dispatcher.OnPermissionDenied; -import permissions.dispatcher.OnShowRationale; -import permissions.dispatcher.PermissionRequest; -import permissions.dispatcher.RuntimePermissions; - @RuntimePermissions public class PasswordActivity extends StylishActivity implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback { @@ -295,7 +282,7 @@ public class PasswordActivity extends StylishActivity protected void onResume() { // If the database isn't accessible make sure to clear the password field, if it // was saved in the instance state - if (App.getDB().loaded) { + if (App.getDB().getLoaded()) { setEmptyViews(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java index 0a42e5e34..76514ef49 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java @@ -60,7 +60,7 @@ public class MainPreferenceFragment extends PreferenceFragmentCompat implements preference = findPreference(getString(R.string.db_key)); preference.setOnPreferenceClickListener(this); Database db = App.getDB(); - if (!(db.loaded)) { + if (!(db.getLoaded())) { preference.setEnabled(false); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index a42c59429..d7fd7dfd6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -322,7 +322,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat case DATABASE: setPreferencesFromResource(R.xml.database_preferences, rootKey); - if (database.loaded) { + if (database.getLoaded()) { PreferenceCategory dbGeneralPrefCategory = (PreferenceCategory) findPreference(getString(R.string.database_general_key)); From f149688da6a10576ec54e44ed10eaf9ae97cb6e9 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Mon, 6 May 2019 10:29:58 +0000 Subject: [PATCH 102/289] Translated using Weblate (Arabic) Currently translated at 71.0% (245 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ar/ --- app/src/main/res/values-ar/strings.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 370677160..e45d9aa9b 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -272,4 +272,21 @@ أنشئ قاعدة بيانات أنشئ ملف إدارة كلمات السر. إفتح قاعدة بيانات + سلة المحذوفات في الأسفل + قاعده بيانات طبيعية + الوصول + إقفال + \"الإعدادات\" ← \"الأمان\" ← \"البصمة\" + تعيين مفتاح رئيسي + استخدم سلة المحذوفات + Magikeyboard + إعدادات Magikeyboard + \"الإعدادات\" ← \"اللغة والإدخال\" ← \"لوحة المفاتيح الحالية\" ثم اختر واحدا. + أو (\"الإعدادات\" ← \"اللغة والإدخال\" ← \"لوحة المفاتيح الافتراضية\" ثم اختر واحدا.) + Magikeyboard + Magikeyboard (KeePass DX) + %1$s متوفر على Magikeyboard + %1$s + إعادة تعيين الشاشات التعليمية + البحث من خلال الإدخالات \ No newline at end of file From 6be2148951d5f59738dbcc13aa6980eb16991f95 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 9 May 2019 23:41:26 +0200 Subject: [PATCH 103/289] Fix opening bugs --- .../com/kunzisoft/keepass/tests/TestUtil.java | 10 --- .../keepass/activities/EntryEditActivity.java | 4 +- .../keepass/adapters/NodeAdapter.java | 2 +- .../AssignPasswordInDatabaseRunnable.kt | 8 +-- .../database/action/CreateDatabaseRunnable.kt | 14 ++-- .../keepass/database/element/Database.kt | 70 ++++++++----------- .../database/element/EntryVersioned.kt | 8 +-- .../database/element/GroupVersioned.kt | 33 +++++---- .../keepass/database/element/PwDatabase.java | 16 +++-- .../database/element/PwDatabaseV3.java | 8 ++- .../database/element/PwDatabaseV4.java | 10 ++- .../keepass/database/element/PwEntryV3.java | 6 -- .../keepass/database/element/PwEntryV4.java | 19 +++-- .../keepass/database/element/PwGroupV4.java | 10 +-- .../keepass/database/load/Importer.java | 7 ++ .../keepass/database/load/ImporterV3.java | 18 +++-- .../keepass/database/load/ImporterV4.java | 11 ++- .../com/kunzisoft/keepass/utils/UriUtil.java | 15 ---- .../com/kunzisoft/keepass/utils/UriUtils.kt | 21 ++++++ 19 files changed, 146 insertions(+), 144 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/UriUtils.kt diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java index c7dd69151..bdec9c505 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java @@ -54,16 +54,6 @@ public class TestUtil { } - public static InputStream getKeyFileInputStream(Context ctx, String keyfile) throws FileNotFoundException { - InputStream keyIs = null; - if (!EmptyUtils.isNullOrEmpty(keyfile)) { - Uri uri = UriUtil.parseDefaultFile(keyfile); - keyIs = UriUtil.getUriInputStream(ctx, uri); - } - - return keyIs; - } - public static String getSdPath(String filename) { File file = new File(sdcard, filename); return file.getAbsolutePath(); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 58fbf3d60..9ed63f747 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -188,8 +188,8 @@ public class EntryEditActivity extends LockingHideActivity fillData(); } - // Close the activity if entry to edit can't be retrieve - if (mEntry == null) { + // Close the activity if entry to edit or parent to add entry can't be retrieve + if (mEntry == null || mParent == null) { finish(); return; } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java index 3dcdd0a43..ccc348600 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java @@ -139,7 +139,7 @@ public class NodeAdapter extends RecyclerView.Adapter { assignPreferences(); // TODO verify sort try { - this.nodeSortedList.addAll(group.getChildEntriesWithoutMetaStream()); + this.nodeSortedList.addAll(group.getChildrenWithoutMetaStream()); } catch (Exception e) { Log.e(TAG, "Can't add node elements to the list", e); Toast.makeText(context, "Can't add node elements to the list : " + e.getMessage(), Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt index 2d294b8cd..90f0d3326 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt @@ -25,6 +25,7 @@ import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.InvalidKeyFileException import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.UriUtil +import com.kunzisoft.keepass.utils.getUriInputStream import java.io.IOException class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( @@ -57,10 +58,9 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( mBackupKey = ByteArray(database.masterKey.size) System.arraycopy(database.masterKey, 0, mBackupKey!!, 0, mBackupKey!!.size) - val uriInputStream = UriUtil.getUriInputStream(context, mKeyFile) - mMasterPassword?.let { - database.retrieveMasterKey(it, uriInputStream) - } + val uriInputStream = getUriInputStream(context.contentResolver, mKeyFile) + database.retrieveMasterKey(mMasterPassword, uriInputStream) + // To save the database super.run() finishRun(true) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt index c4d32c3cc..3ab478d89 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt @@ -22,7 +22,6 @@ package com.kunzisoft.keepass.database.action import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.tasks.ActionRunnable -import com.kunzisoft.keepass.utils.UriUtil class CreateDatabaseRunnable(private val mFilename: String, val onDatabaseCreate: (database: Database) -> ActionRunnable) @@ -35,14 +34,11 @@ class CreateDatabaseRunnable(private val mFilename: String, // Create new database record database = Database(mFilename) App.setDB(database) - - // Set Database state - database?.setUri(UriUtil.parseDefaultFile(mFilename)) - database?.loaded = true - - // Commit changes - database?.let { - onDatabaseCreate(it).run() + database?.apply { + // Set Database state + loaded = true + // Commit changes + onDatabaseCreate(this).run() } finishRun(true) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index bf349fc54..ed8586d83 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -41,6 +41,7 @@ import com.kunzisoft.keepass.stream.LEDataInputStream import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.utils.EmptyUtils import com.kunzisoft.keepass.utils.UriUtil +import com.kunzisoft.keepass.utils.getUriInputStream import org.apache.commons.io.FileUtils import java.io.* import java.util.* @@ -176,20 +177,22 @@ class Database { GroupVersioned(pwDatabaseV4!!.recycleBin!!) } else null - constructor() {} + constructor() constructor(databasePath: String) { // TODO Test with kdb extension if (isKDBExtension(databasePath)) { setDatabaseV3(PwDatabaseV3()) } else { - val groupV4 = PwGroupV4() - setDatabaseV4(PwDatabaseV4()) - + val databaseV4 = PwDatabaseV4() + val groupV4 = databaseV4.createGroup() groupV4.title = dbNameFromPath(databasePath) - groupV4.setIconStandard(pwDatabaseV4!!.getIconFactory().folderIcon) - this.pwDatabaseV4!!.rootGroup = groupV4 + groupV4.setIconStandard(databaseV4.getIconFactory().folderIcon) + databaseV4.rootGroup = groupV4 + setDatabaseV4(databaseV4) } + + setUri(UriUtil.parseDefaultFile(databasePath)) } private fun setDatabaseV3(pwDatabaseV3: PwDatabaseV3) { @@ -236,9 +239,9 @@ class Database { } // Pass Uris as InputStreams - val inputStream: InputStream + val inputStream: InputStream? try { - inputStream = UriUtil.getUriInputStream(ctx, uri) + inputStream = getUriInputStream(ctx.contentResolver, uri) } catch (e: Exception) { Log.e("KPD", "Database::loadData", e) throw ContentFileNotFoundException.getInstance(uri) @@ -248,7 +251,7 @@ class Database { var keyFileInputStream: InputStream? = null keyfile?.let { try { - keyFileInputStream = UriUtil.getUriInputStream(ctx, keyfile) + keyFileInputStream = getUriInputStream(ctx.contentResolver, keyfile) } catch (e: Exception) { Log.e("KPD", "Database::loadData", e) throw ContentFileNotFoundException.getInstance(keyfile) @@ -271,18 +274,21 @@ class Database { // Return to the start bufferedInputStream.reset() + when { // Header of database V3 - PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3().openDatabase(bufferedInputStream, - password, - keyFileInputStream, - progressTaskUpdater)) + PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3() + .openDatabaseAndBuildIndex(bufferedInputStream, + password, + keyFileInputStream, + progressTaskUpdater)) // Header of database V4 - PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(ctx.filesDir).openDatabase(bufferedInputStream, - password, - keyFileInputStream, - progressTaskUpdater)) + PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(ctx.filesDir) + .openDatabaseAndBuildIndex(bufferedInputStream, + password, + keyFileInputStream, + progressTaskUpdater)) // Header not recognized else -> throw InvalidDBSignatureException() @@ -495,24 +501,14 @@ class Database { } @Throws(InvalidKeyFileException::class, IOException::class) - fun retrieveMasterKey(key: String, keyInputStream: InputStream) { + fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) { pwDatabaseV3?.retrieveMasterKey(key, keyInputStream) pwDatabaseV4?.retrieveMasterKey(key, keyInputStream) } fun createEntry(): EntryVersioned? { - - pwDatabaseV3?.let { database -> - return EntryVersioned(PwEntryV3()).apply { - database.newEntryId()?.let { - nodeId = it - } - } - } - - - pwDatabaseV4?.let { database -> - return EntryVersioned(PwEntryV4()).apply { + pwDatabaseV3 ?: pwDatabaseV4?.let { database -> + return EntryVersioned(database.createEntry()).apply { database.newEntryId()?.let { nodeId = it } @@ -523,18 +519,8 @@ class Database { } fun createGroup(): GroupVersioned? { - - pwDatabaseV3?.let { database -> - return GroupVersioned(PwGroupV3()).apply { - database.newGroupId()?.let { - setNodeId(it) - } - } - } - - - pwDatabaseV4?.let { database -> - return GroupVersioned(PwGroupV4()).apply { + pwDatabaseV3 ?: pwDatabaseV4?.let { database -> + return GroupVersioned(database.createGroup()).apply { database.newGroupId()?.let { setNodeId(it) } 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 eb7fc77c9..b49dae1b9 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 @@ -25,11 +25,11 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { */ constructor(entry: EntryVersioned) { if (entry.pwEntryV3 != null) { - if (this.pwEntryV3 != null) + if (this.pwEntryV3 == null) this.pwEntryV3 = PwEntryV3() } if (entry.pwEntryV4 != null) { - if (this.pwEntryV4 != null) + if (this.pwEntryV4 == null) this.pwEntryV4 = PwEntryV4() } updateWith(entry) @@ -37,14 +37,14 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { constructor(entry: PwEntryV3) { this.pwEntryV4 = null - if (this.pwEntryV3 != null) + if (this.pwEntryV3 == null) this.pwEntryV3 = PwEntryV3() this.pwEntryV3?.updateWith(entry) } constructor(entry: PwEntryV4) { this.pwEntryV3 = null - if (this.pwEntryV4 != null) + if (this.pwEntryV4 == null) this.pwEntryV4 = PwEntryV4() this.pwEntryV4?.updateWith(entry) } 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 a4e7a3194..c932b8773 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 @@ -25,8 +25,8 @@ class GroupVersioned : NodeVersioned, PwGroupInterface { - return ArrayList() // TODO if needed + val children = ArrayList() + + pwGroupV3?:pwGroupV4?.getChildGroups()?.forEach { + children.add(GroupVersioned(it)) + } + + return children } override fun getChildEntries(): MutableList { val children = ArrayList() - pwGroupV3?.getChildEntries()?.forEach { - children.add(EntryVersioned(it)) - } - - pwGroupV4?.getChildEntries()?.forEach { + pwGroupV3?:pwGroupV4?.getChildEntries()?.forEach { children.add(EntryVersioned(it)) } @@ -188,17 +190,20 @@ class GroupVersioned : NodeVersioned, PwGroupInterface? { - pwGroupV3?.let { - return getChildEntries().filter { !it.isMetaStream } - } + fun getChildrenWithoutMetaStream(): List { + val children = ArrayList() + children.addAll(getChildGroups()) + + pwGroupV3?.let { + children.addAll(getChildEntries().filter { !it.isMetaStream }) + } pwGroupV4?.let { // No MetasStream in V4 - return getChildEntries() + children.addAll(getChildEntries()) } - return null + return children } override fun addChildGroup(group: GroupVersioned) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index b236523b0..07028a7a1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -37,14 +37,14 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.UUID; -public abstract class PwDatabase { +public abstract class PwDatabase, Entry extends PwEntry> { public static final UUID UUID_ZERO = new UUID(0,0); // Algorithm used to encrypt the database protected PwEncryptionAlgorithm algorithm; - protected byte masterKey[] = new byte[32]; + protected byte[] masterKey = new byte[32]; protected byte[] finalKey; protected PwIconFactory iconFactory = new PwIconFactory(); @@ -70,15 +70,15 @@ public abstract class PwDatabase { return finalKey; } - protected abstract byte[] getMasterKey(String key, InputStream keyInputStream) + protected abstract byte[] getMasterKey(@Nullable String key, @Nullable InputStream keyInputStream) throws InvalidKeyFileException, IOException; - public void retrieveMasterKey(String key, InputStream keyInputStream) + public void retrieveMasterKey(@Nullable String key, @Nullable InputStream keyInputStream) throws InvalidKeyFileException, IOException { - masterKey = getMasterKey(key, keyInputStream); - } + masterKey = getMasterKey(key, keyInputStream); + } - protected byte[] getCompositeKey(String key, InputStream keyInputStream) + protected byte[] getCompositeKey(@Nullable String key, @Nullable InputStream keyInputStream) throws InvalidKeyFileException, IOException { assert(key != null && keyInputStream != null); @@ -239,6 +239,8 @@ public abstract class PwDatabase { public abstract Group getRootGroup(); + public abstract Entry createEntry(); + /* * ------------------------------------- * Index Manipulation diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index 07bcaa97e..f1bfbbcfe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -24,6 +24,7 @@ import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.stream.NullOutputStream; +import javax.annotation.Nullable; import java.io.IOException; import java.io.InputStream; import java.security.DigestOutputStream; @@ -146,7 +147,7 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public byte[] getMasterKey(String key, InputStream keyInputStream) + public byte[] getMasterKey(@Nullable String key, @Nullable InputStream keyInputStream) throws InvalidKeyFileException, IOException { if (key != null && keyInputStream != null) { @@ -227,6 +228,11 @@ public class PwDatabaseV3 extends PwDatabase { return rootGroup; } + @Override + public PwEntryV3 createEntry() { + return new PwEntryV3(); + } + // TODO: This could still be refactored cleaner public void copyEncrypted(byte[] buf, int offset, int size) { // No-op diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 5a88c944b..f05bfd5da 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -367,7 +367,7 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - public byte[] getMasterKey(String key, InputStream keyInputStream) + public byte[] getMasterKey(@Nullable String key, @Nullable InputStream keyInputStream) throws InvalidKeyFileException, IOException { byte[] fKey = new byte[]{}; @@ -523,6 +523,12 @@ public class PwDatabaseV4 extends PwDatabase { return rootGroup; } + + @Override + public PwEntryV4 createEntry() { + return new PwEntryV4(); + } + @Override public boolean isBackup(PwGroupV4 group) { if (!recycleBinEnabled) { @@ -540,7 +546,7 @@ public class PwDatabaseV4 extends PwDatabase { if (getRecycleBin() == null) { // Create recycle bin - PwGroupV4 recycleBin = new PwGroupV4(); + PwGroupV4 recycleBin = createGroup(); recycleBin.setTitle(RECYCLEBIN_NAME); recycleBin.setIconStandard(iconFactory.getTrashIcon()); recycleBin.setEnableAutoType(false); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index f5b3c0195..97d5f1737 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -187,12 +187,6 @@ public class PwEntryV3 extends PwEntry { return Type.ENTRY; } - public void setNewParent(int groupId) { - PwGroupV3 pwGroupV3 = new PwGroupV3(); - pwGroupV3.setNodeId(new PwNodeIdInt(groupId)); - setParent(pwGroupV3); - } - @Override public String getUsername() { return username; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 4f90fd83e..df369158e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -173,6 +173,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLog return newEntry; } + @NonNull @Override public Type getType() { return Type.ENTRY; @@ -202,47 +203,50 @@ public class PwEntryV4 extends PwEntry implements ITimeLog return spr.compile(text, this, db); } + @NonNull @Override public String getUsername() { return decodeRefKey(mDecodeRef, STR_USERNAME); } + @NonNull @Override public String getTitle() { return decodeRefKey(mDecodeRef, STR_TITLE); } + @NonNull @Override public String getPassword() { return decodeRefKey(mDecodeRef, STR_PASSWORD); } @Override - public void setTitle(String title) { + public void setTitle(@NonNull String title) { boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectTitle; setProtectedString(STR_TITLE, title, protect); } @Override - public void setUsername(String user) { + public void setUsername(@NonNull String user) { boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectUserName; setProtectedString(STR_USERNAME, user, protect); } @Override - public void setPassword(String pass) { + public void setPassword(@NonNull String pass) { boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectPassword; setProtectedString(STR_PASSWORD, pass, protect); } @Override - public void setUrl(String url) { + public void setUrl(@NonNull String url) { boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectUrl; setProtectedString(STR_URL, url, protect); } @Override - public void setNotes(String notes) { + public void setNotes(@NonNull String notes) { boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectNotes; setProtectedString(STR_NOTES, notes, protect); } @@ -271,17 +275,20 @@ public class PwEntryV4 extends PwEntry implements ITimeLog usageCount = count; } + @NonNull @Override public String getNotes() { return decodeRefKey(mDecodeRef, STR_NOTES); } + @NonNull @Override public String getUrl() { return decodeRefKey(mDecodeRef, STR_URL); } - @Override + @NonNull + @Override public PwIcon getIcon() { if (customIcon == null || customIcon.isUnknown()) { return super.getIcon(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 14b87d057..cc490ec1e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -20,6 +20,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import android.support.annotation.NonNull; import com.kunzisoft.keepass.database.ITimeLogger; import java.util.HashMap; @@ -74,7 +75,7 @@ public class PwGroupV4 extends PwGroup implements IT } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(customIcon, flags); dest.writeLong(usageCount); @@ -118,7 +119,6 @@ public class PwGroupV4 extends PwGroup implements IT lastTopVisibleEntry = source.lastTopVisibleEntry; } - @SuppressWarnings("unchecked") @Override public PwGroupV4 clone() { // Attributes in parent @@ -143,7 +143,8 @@ public class PwGroupV4 extends PwGroup implements IT return newGroup; } - @Override + @NonNull + @Override public Type getType() { return Type.GROUP; } @@ -184,7 +185,8 @@ public class PwGroupV4 extends PwGroup implements IT expires = exp; } - @Override + @NonNull + @Override public PwIcon getIcon() { if (customIcon == null || customIcon.isUnknown()) { // TODO Encapsulate with PwEntryV4 return super.getIcon(); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java index 1b60c2e88..ff1d1bbdb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java @@ -41,4 +41,11 @@ public abstract class Importer { public abstract PwDb openDatabase(InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater updater) throws IOException, InvalidDBException; + public PwDb openDatabaseAndBuildIndex(InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater updater) + throws IOException, InvalidDBException { + PwDb database = openDatabase(inStream, password, keyInputStream, updater); + database.populateNodesIndexes(); + return database; + } + } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index b8ad4b184..d80b17350 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -186,13 +186,13 @@ public class ImporterV3 extends Importer { } // New manual root because V3 contains multiple root groups (here available with getRootGroups()) - PwGroupV3 newRoot = new PwGroupV3(); + PwGroupV3 newRoot = databaseToOpen.createGroup(); newRoot.setLevel(-1); databaseToOpen.setRootGroup(newRoot); // Import all groups int pos = PwDbHeaderV3.BUF_SIZE; - PwGroupV3 newGrp = new PwGroupV3(); + PwGroupV3 newGrp = databaseToOpen.createGroup(); for( int i = 0; i < hdr.numGroups; ) { int fieldType = LEDataInputStream.readUShort( filebuf, pos ); pos += 2; @@ -202,7 +202,7 @@ public class ImporterV3 extends Importer { if( fieldType == 0xFFFF ) { // End-Group record. Save group and count it. databaseToOpen.addGroupIndex(newGrp); - newGrp = new PwGroupV3(); + newGrp = databaseToOpen.createGroup(); i++; } else { @@ -212,7 +212,7 @@ public class ImporterV3 extends Importer { } // Import all entries - PwEntryV3 newEnt = new PwEntryV3(); + PwEntryV3 newEnt = databaseToOpen.createEntry(); for( int i = 0; i < hdr.numEntries; ) { int fieldType = LEDataInputStream.readUShort( filebuf, pos ); int fieldSize = LEDataInputStream.readInt( filebuf, pos + 2 ); @@ -220,7 +220,7 @@ public class ImporterV3 extends Importer { if( fieldType == 0xFFFF ) { // End-Group record. Save group and count it. databaseToOpen.addEntryIndex(newEnt); - newEnt = new PwEntryV3(); + newEnt = databaseToOpen.createEntry(); i++; } else { @@ -228,8 +228,6 @@ public class ImporterV3 extends Importer { } pos += 2 + 4 + fieldSize; } - - databaseToOpen.populateNodesIndexes(); return databaseToOpen; } @@ -276,8 +274,6 @@ public class ImporterV3 extends Importer { } } - - private void readEntryField(PwDatabaseV3 db, PwEntryV3 ent, byte[] buf, int offset) throws UnsupportedEncodingException { int fieldType = LEDataInputStream.readUShort(buf, offset); offset += 2; @@ -292,7 +288,9 @@ public class ImporterV3 extends Importer { ent.setNodeId(new PwNodeIdUUID(Types.bytestoUUID(buf, offset))); break; case 0x0002 : - ent.setNewParent(LEDataInputStream.readInt(buf, offset)); + PwGroupV3 pwGroupV3 = databaseToOpen.createGroup(); + pwGroupV3.setNodeId(new PwNodeIdInt(LEDataInputStream.readInt(buf, offset))); + ent.setParent(pwGroupV3); break; case 0x0003 : int iconId = LEDataInputStream.readInt(buf, offset); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 43245cfc8..d571bbc07 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -175,8 +175,6 @@ public class ImporterV4 extends Importer { ReadXmlStreamed(isXml); - mDatabase.populateNodesIndexes(); - return mDatabase; } @@ -512,9 +510,8 @@ public class ImporterV4 extends Importer { if ( ctxGroups.size() != 0 ) throw new IOException("Group list should be empty."); - PwGroupV4 rootGroup = new PwGroupV4(); - mDatabase.setRootGroup(rootGroup); - ctxGroups.push(rootGroup); + mDatabase.setRootGroup(mDatabase.createGroup()); + ctxGroups.push(mDatabase.getRootGroup()); ctxGroup = ctxGroups.peek(); return SwitchContext(ctx, KdbContext.Group, xpp); @@ -551,7 +548,7 @@ public class ImporterV4 extends Importer { } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData) ) { return SwitchContext(ctx, KdbContext.GroupCustomData, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - ctxGroup = new PwGroupV4(); + ctxGroup = mDatabase.createGroup(); PwGroupV4 groupPeek = ctxGroups.peek(); groupPeek.addChildGroup(ctxGroup); ctxGroup.setParent(groupPeek); @@ -559,7 +556,7 @@ public class ImporterV4 extends Importer { return SwitchContext(ctx, KdbContext.Group, xpp); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { - ctxEntry = new PwEntryV4(); + ctxEntry = mDatabase.createEntry(); ctxGroup.addChildEntry(ctxEntry); ctxEntry.setParent(ctxGroup); diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java index 475bcc178..91b4838a8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java @@ -61,21 +61,6 @@ public class UriUtil { return left.equals(uriRight); } - public static InputStream getUriInputStream(Context ctx, Uri uri) throws FileNotFoundException { - if (uri == null) return null; - - String scheme = uri.getScheme(); - if (EmptyUtils.isNullOrEmpty(scheme) || scheme.equals("file")) { - return new FileInputStream(uri.getPath()); - } - else if (scheme.equals("content")) { - return ctx.getContentResolver().openInputStream(uri); - } - else { - return null; - } - } - /** * Many android apps respond with non-writeable content URIs that correspond to files. * This will attempt to translate the content URIs to file URIs when possible/appropriate diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtils.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtils.kt new file mode 100644 index 000000000..0f0df99d3 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtils.kt @@ -0,0 +1,21 @@ +package com.kunzisoft.keepass.utils + +import android.content.ContentResolver +import android.net.Uri +import java.io.FileInputStream +import java.io.FileNotFoundException +import java.io.InputStream + +@Throws(FileNotFoundException::class) +fun getUriInputStream(contentResolver: ContentResolver, uri: Uri?): InputStream? { + if (uri == null) return null + + val scheme = uri.scheme + return if (EmptyUtils.isNullOrEmpty(scheme) || scheme == "file") { + FileInputStream(uri.path!!) + } else if (scheme == "content") { + contentResolver.openInputStream(uri) + } else { + null + } +} \ No newline at end of file From e75a2502d12354e962226755f89e8771e31da4c2 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 10 May 2019 00:39:31 +0200 Subject: [PATCH 104/289] Better index implementation --- .../keepass/activities/EntryEditActivity.java | 18 ++++--------- .../keepass/database/element/Database.kt | 19 +++++++------- .../database/element/EntryVersioned.kt | 8 ++---- .../database/element/GroupVersioned.kt | 8 ++---- .../keepass/database/element/PwDatabase.java | 10 +++----- .../database/element/PwDatabaseV3.java | 3 +-- .../database/element/PwDatabaseV4.java | 25 +++---------------- .../keepass/database/load/Importer.java | 7 ------ .../keepass/database/load/ImporterV3.java | 4 ++- .../keepass/database/load/ImporterV4.java | 4 +++ 10 files changed, 33 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 9ed63f747..4d1cca3e3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -26,17 +26,8 @@ import android.graphics.Color; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.Toast; - +import android.view.*; +import android.widget.*; import com.getkeepsafe.taptargetview.TapTarget; import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; @@ -56,7 +47,6 @@ import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.Util; import com.kunzisoft.keepass.view.EntryEditCustomField; - import org.jetbrains.annotations.NotNull; import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD; @@ -185,7 +175,8 @@ public class EntryEditActivity extends LockingHideActivity } else { mEntry = database.getEntryById(keyEntry); mIsNew = false; - fillData(); + if (mEntry != null) + fillData(); } // Close the activity if entry to edit or parent to add entry can't be retrieve @@ -525,6 +516,7 @@ public class EntryEditActivity extends LockingHideActivity if (mEntry.allowExtraFields()) { LinearLayout container = findViewById(R.id.entry_edit_advanced_container); + // TODO Close here but why ? mEntry.getFields().doActionToAllCustomProtectedField((key, value) -> { EntryEditCustomField entryEditCustomField = new EntryEditCustomField(EntryEditActivity.this); entryEditCustomField.setData(key, value); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index ed8586d83..ed310238a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -168,14 +168,15 @@ class Database { get() = pwDatabaseV4 != null val isRecycleBinEnabled: Boolean - get() = if (pwDatabaseV4 != null) { - pwDatabaseV4!!.isRecycleBinEnabled - } else false + get() = pwDatabaseV4?.isRecycleBinEnabled ?: false val recycleBin: GroupVersioned? - get() = if (pwDatabaseV4 != null) { - GroupVersioned(pwDatabaseV4!!.recycleBin!!) - } else null + get() { + pwDatabaseV4?.recycleBin?.let { + return GroupVersioned(it) + } + return null + } constructor() @@ -278,14 +279,14 @@ class Database { when { // Header of database V3 PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3() - .openDatabaseAndBuildIndex(bufferedInputStream, + .openDatabase(bufferedInputStream, password, keyFileInputStream, progressTaskUpdater)) // Header of database V4 PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(ctx.filesDir) - .openDatabaseAndBuildIndex(bufferedInputStream, + .openDatabase(bufferedInputStream, password, keyFileInputStream, progressTaskUpdater)) @@ -544,11 +545,9 @@ class Database { pwDatabaseV3?.getGroupById(id)?.let { return GroupVersioned(it) } - pwDatabaseV4?.getGroupById(id)?.let { return GroupVersioned(it) } - return null } 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 b49dae1b9..51cae5b44 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 @@ -37,16 +37,12 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { constructor(entry: PwEntryV3) { this.pwEntryV4 = null - if (this.pwEntryV3 == null) - this.pwEntryV3 = PwEntryV3() - this.pwEntryV3?.updateWith(entry) + this.pwEntryV3 = entry } constructor(entry: PwEntryV4) { this.pwEntryV3 = null - if (this.pwEntryV4 == null) - this.pwEntryV4 = PwEntryV4() - this.pwEntryV4?.updateWith(entry) + this.pwEntryV4 = entry } constructor(parcel: Parcel) { 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 c932b8773..bd51e504f 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 @@ -34,16 +34,12 @@ class GroupVersioned : NodeVersioned, PwGroupInterface, Entry e * ------------------------------------- */ - public abstract void populateNodesIndexes(); - public abstract PwNodeId newGroupId(); public abstract PwNodeId newEntryId(); @@ -315,7 +313,7 @@ public abstract class PwDatabase, Entry e * ------------------------------------- */ - protected void addGroupTo(Group newGroup, @Nullable Group parent) { + public void addGroupTo(Group newGroup, @Nullable Group parent) { // Add tree to parent tree if (parent != null) parent.addChildGroup(newGroup); @@ -323,7 +321,7 @@ public abstract class PwDatabase, Entry e addGroupIndex(newGroup); } - protected void removeGroupFrom(Group groupToRemove, Group parent) { + public void removeGroupFrom(Group groupToRemove, Group parent) { // Remove tree from parent tree if (parent != null) { parent.removeChildGroup(groupToRemove); @@ -331,7 +329,7 @@ public abstract class PwDatabase, Entry e removeGroupIndex(groupToRemove); } - protected void addEntryTo(Entry newEntry, @Nullable Group parent) { + public void addEntryTo(Entry newEntry, @Nullable Group parent) { // Add entry to parent if (parent != null) parent.addChildEntry(newEntry); @@ -339,7 +337,7 @@ public abstract class PwDatabase, Entry e addEntryIndex(newEntry); } - protected void removeEntryFrom(Entry entryToRemove, Group parent) { + public void removeEntryFrom(Entry entryToRemove, Group parent) { // Remove entry for parent if (parent != null) { parent.removeChildEntry(entryToRemove); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index f1bfbbcfe..5d5883141 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -111,8 +111,7 @@ public class PwDatabaseV3 extends PwDatabase { } } - @Override - public void populateNodesIndexes() { + public void constructTreeFromIndex() { constructTreeFromIndex(getRootGroup()); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index f05bfd5da..fd4ab2624 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -470,25 +470,6 @@ public class PwDatabaseV4 extends PwDatabase { return null; } - @Override - public void populateNodesIndexes() { - getRootGroup().doForEachChildAndForIt( - new NodeHandler() { - @Override - public boolean operate(PwEntryV4 entry) { - addEntryIndex(entry); - return true; - } - }, - new NodeHandler() { - @Override - public boolean operate(PwGroupV4 group) { - addGroupIndex(group); - return true; - } - }); - } - @Override public PwNodeIdUUID newGroupId() { PwNodeIdUUID newId; @@ -656,15 +637,15 @@ public class PwDatabaseV4 extends PwDatabase { } @Override - protected void removeEntryFrom(PwEntryV4 entryToRemove, PwGroupV4 parent) { + public void removeEntryFrom(PwEntryV4 entryToRemove, PwGroupV4 parent) { super.removeEntryFrom(entryToRemove, parent); - deletedObjects.add(new PwDeletedObject((UUID) entryToRemove.getNodeId().getId())); + deletedObjects.add(new PwDeletedObject(entryToRemove.getNodeId().getId())); } @Override public void undoDeleteEntryFrom(PwEntryV4 entry, PwGroupV4 origParent) { super.undoDeleteEntryFrom(entry, origParent); - deletedObjects.remove(new PwDeletedObject((UUID) entry.getNodeId().getId())); + deletedObjects.remove(new PwDeletedObject(entry.getNodeId().getId())); } public PwGroupV4 getRecycleBin() { // TODO delete recycle bin preference diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java index ff1d1bbdb..1b60c2e88 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java @@ -41,11 +41,4 @@ public abstract class Importer { public abstract PwDb openDatabase(InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater updater) throws IOException, InvalidDBException; - public PwDb openDatabaseAndBuildIndex(InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater updater) - throws IOException, InvalidDBException { - PwDb database = openDatabase(inStream, password, keyInputStream, updater); - database.populateNodesIndexes(); - return database; - } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index d80b17350..83aaa6301 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -228,7 +228,9 @@ public class ImporterV3 extends Importer { } pos += 2 + 4 + fieldSize; } - + + databaseToOpen.constructTreeFromIndex(); + return databaseToOpen; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index d571bbc07..86922749a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -525,6 +525,7 @@ public class ImporterV4 extends Importer { case Group: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { ctxGroup.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); + mDatabase.addGroupIndex(ctxGroup); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemName) ) { ctxGroup.setTitle(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemNotes) ) { @@ -587,6 +588,7 @@ public class ImporterV4 extends Importer { case Entry: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { ctxEntry.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); + mDatabase.addEntryIndex(ctxEntry); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { ctxEntry.setIconStandard(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { @@ -789,6 +791,7 @@ public class ImporterV4 extends Importer { } else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { if ( ctxGroup.getNodeId() == null || ctxGroup.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { ctxGroup.setNodeId(new PwNodeIdUUID()); + mDatabase.addGroupIndex(ctxGroup); } ctxGroups.pop(); @@ -817,6 +820,7 @@ public class ImporterV4 extends Importer { } else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { if ( ctxEntry.getNodeId() == null || ctxEntry.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { ctxEntry.setNodeId(new PwNodeIdUUID()); + mDatabase.addEntryIndex(ctxEntry); } if ( entryInHistory ) { From 548039c66f365b7f2ca11b409ca089fa85b12f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 15 May 2019 15:06:56 +0000 Subject: [PATCH 105/289] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 99.1% (342 of 345 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 | 34 ++++++++------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 4c45c4b79..2beff808e 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -1,7 +1,7 @@ - Tilbakemelding: - Hjemmeside: + Tilbakemelding + Hjemmeside Android-implementasjon av KeePass-passordsbehandleren Godta Legg til oppføring @@ -102,7 +102,7 @@ Skjul passord som forvalg Om Endre hovednøkkel - Kopi av %1$s + Kopierte %1$s Innstillinger Programinnstillinger Skjemautfylling @@ -251,7 +251,6 @@ Tekstutseende Programutseende Annet - Tastatur Magikeyboard Aktiver et egendefinert tastatur som fyller inn passordene og alle identitetsfelter. @@ -266,12 +265,10 @@ Fyll inn feltene ved bruk av elementene i oppføringen. Lås databasen. Gå tilbake til ditt hovedtastatur. - Tillat inget passord Skru på \"Åpne\"-tasten hvis ingen passordidentifikasjon er valgt. Skrivebeskyttet - "Åpne din database skrivebeskyttet som forvalg." - + Åpne din database skrivebeskyttet som forvalg. Hjelpeskjermer Framhev elementer for opplæring i programmet Tilbakestill opplæringsskjermer @@ -316,12 +313,12 @@ Sorter oppføringer og grupper i henhold til spesifikke parameter. Delta Delta for å øke stabiliteten, sikkerheten, og med å legge til flere funksjoner. - Ulikt mange passordbehandlingsprogrammer, er dette reklamefri, copyleftbasert fri programvare og samler ikke inn personlig data på tjenerne sine, selv i sin gratisversjon. - Ved kjøp av pro-versjonen, vil du få tilgang til denne visuelle funksjonen og du vil spesielt hjelpe realiseringen av gemenskapsprosjekter. + Ved kjøp av pro-versjonen, vil du få tilgang til denne visuelle funksjonen og du vil spesielt hjelpe realiseringen av gemenskapsprosjekter. + Denne visuelle funksjonen er tilgjengelig takket være din generøsitet. - For å beholde vår frihet og alltid være aktive, stoler vi på dine bidrag. - + For å beholde vår frihet og alltid være aktive, stoler vi på dine bidrag. + Denne funksjonen er under utvikling og krever bidrag for å bli tilgjengelig snart. Ved å kjøpe pro-versjonen, Ved å bidra, @@ -329,45 +326,34 @@ Takk for ditt bidrag. Det arbeides hardt på realiseringen av denne utgaven. Ikke glem å holde programmet ditt oppgradert. - Last ned Bidra - Rijndael (AES) Twofish ChaCha20 - AES KDF Argon2 - Velg en drakt Tilpass programdrakten ved å endre fargene Velg en ikonpakke Endre programmets ikonpakke - -Bygg %1$s + Bygg %1$s Magikeyboard Magikeyboard (KeePass DX) Magikeyboard-innstillinger - Oppføring - Tidsavbrudd Tidsavbrudd for tømming av tastaturoppføring - Merknadsinfo Vis en merknad når en oppføring er tilgjengelig Oppføring %1$s tilgjengelig på Magikeyboard %1$s - Tøm ved lukking Tøm tastaturoppføringen ved lukking av merknaden Utseende - Tastaturdrakt - Taster Vibrer ved tastetrykk Lyd ved tastetrykk - \ No newline at end of file + \ No newline at end of file From af316607e1182d5499be7f158c04811f483ee4f3 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 30 May 2019 21:11:33 +0200 Subject: [PATCH 106/289] Fix copy and clone --- .../keepass/activities/EntryEditActivity.java | 55 +++++++------- .../kunzisoft/keepass/database/AutoType.java | 17 ++++- .../keepass/database/ExtraFields.java | 14 ++-- .../database/action/node/CopyEntryRunnable.kt | 2 +- .../action/node/DeleteGroupRunnable.java | 75 ------------------- .../action/node/DeleteGroupRunnable.kt | 61 +++++++++++++++ .../database/action/node/MoveEntryRunnable.kt | 4 +- .../database/action/node/MoveGroupRunnable.kt | 4 +- .../action/node/UpdateEntryRunnable.kt | 9 ++- .../keepass/database/element/Database.kt | 59 ++++++++++----- .../database/element/EntryVersioned.kt | 27 ++++--- .../database/element/PwDatabaseV3.java | 4 +- .../database/element/PwDatabaseV4.java | 4 +- .../keepass/database/element/PwDate.java | 9 ++- .../keepass/database/element/PwEntryV3.java | 7 +- .../keepass/database/element/PwEntryV4.java | 71 +++++++----------- .../keepass/database/element/PwGroupV3.java | 5 ++ .../keepass/database/element/PwGroupV4.java | 17 ++++- .../keepass/database/element/PwNode.java | 11 +-- .../keepass/database/element/PwNodeIdInt.java | 5 +- .../database/element/PwNodeIdUUID.java | 2 +- .../keepass/database/load/ImporterV4.java | 6 +- .../database/security/ProtectedBinary.java | 15 +++- 23 files changed, 267 insertions(+), 216 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 4d1cca3e3..6d3483eeb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -71,7 +71,7 @@ public class EntryEditActivity extends LockingHideActivity protected EntryVersioned mEntry; protected GroupVersioned mParent; - protected EntryVersioned mCallbackNewEntry; + protected EntryVersioned mNewEntry; protected boolean mIsNew; protected PwIconStandard mSelectedIconStandard; @@ -155,9 +155,6 @@ public class EntryEditActivity extends LockingHideActivity // Likely the app has been killed exit the activity database = App.getDB(); - Intent intent = getIntent(); - PwNodeId keyEntry = intent.getParcelableExtra(KEY_ENTRY); - // Retrieve the textColor to tint the icon int[] attrs = {android.R.attr.textColorPrimary}; TypedArray ta = getTheme().obtainStyledAttributes(attrs); @@ -165,21 +162,29 @@ public class EntryEditActivity extends LockingHideActivity mSelectedIconStandard = database.getIconFactory().getUnknownIcon(); - if (keyEntry == null) { - PwNodeId parentId = intent.getParcelableExtra(KEY_PARENT); - mParent = database.getGroupById(parentId); + Intent intent = getIntent(); + // Entry is retrieve, it's an entry to update + PwNodeId keyEntry = intent.getParcelableExtra(KEY_ENTRY); + if (keyEntry != null) { + mIsNew = false; + mEntry = database.getEntryById(keyEntry); + if (mEntry != null) { + mParent = mEntry.getParent(); + fillData(); + } + } + + // Parent is retrieve, it's a new entry to create + PwNodeId keyParent = intent.getParcelableExtra(KEY_PARENT); + if (keyParent != null) { + mIsNew = true; mEntry = database.createEntry(); - mIsNew = true; + mParent = database.getGroupById(keyParent); // Add the default icon database.getDrawFactory().assignDefaultDatabaseIconTo(this, entryIconView, iconColor); - } else { - mEntry = database.getEntryById(keyEntry); - mIsNew = false; - if (mEntry != null) - fillData(); } - // Close the activity if entry to edit or parent to add entry can't be retrieve + // Close the activity if entry or parent can't be retrieve if (mEntry == null || mParent == null) { finish(); return; @@ -245,7 +250,10 @@ public class EntryEditActivity extends LockingHideActivity if (!validateBeforeSaving()) { return; } - mCallbackNewEntry = populateNewEntry(); + // Clone the entry + mNewEntry = new EntryVersioned(mEntry); + + populateEntryWithViewInfo(mNewEntry); // Open a progress dialog and save entry ActionRunnable task; @@ -260,7 +268,7 @@ public class EntryEditActivity extends LockingHideActivity if ( mIsNew ) { task = new AddEntryRunnable(EntryEditActivity.this, database, - mCallbackNewEntry, + mNewEntry, mParent, afterActionNodeFinishRunnable, !getReadOnly()); @@ -268,7 +276,7 @@ public class EntryEditActivity extends LockingHideActivity task = new UpdateEntryRunnable(EntryEditActivity.this, database, mEntry, - mCallbackNewEntry, + mNewEntry, afterActionNodeFinishRunnable, !getReadOnly()); } @@ -400,13 +408,9 @@ public class EntryEditActivity extends LockingHideActivity return errorValidation.isValidate; } - protected EntryVersioned populateNewEntry() { - Database database = App.getDB(); - - EntryVersioned newEntry = new EntryVersioned(mEntry); + private void populateEntryWithViewInfo(EntryVersioned newEntry) { database.startManageEntry(newEntry); - database.createBackupOf(newEntry); newEntry.setLastAccessTime(new PwDate()); newEntry.setLastModificationTime(new PwDate()); @@ -433,8 +437,6 @@ public class EntryEditActivity extends LockingHideActivity } database.stopManageEntry(newEntry); - - return newEntry; } /** @@ -516,7 +518,6 @@ public class EntryEditActivity extends LockingHideActivity if (mEntry.allowExtraFields()) { LinearLayout container = findViewById(R.id.entry_edit_advanced_container); - // TODO Close here but why ? mEntry.getFields().doActionToAllCustomProtectedField((key, value) -> { EntryEditCustomField entryEditCustomField = new EntryEditCustomField(EntryEditActivity.this); entryEditCustomField.setData(key, value); @@ -559,10 +560,10 @@ public class EntryEditActivity extends LockingHideActivity public void finish() { // Assign entry callback as a result in all case try { - if (mCallbackNewEntry != null) { + if (mNewEntry != null) { Bundle bundle = new Bundle(); Intent intentEntry = new Intent(); - bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, mCallbackNewEntry); + bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, mNewEntry); intentEntry.putExtras(bundle); if (mIsNew) { setResult(ADD_ENTRY_RESULT_CODE, intentEntry); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java b/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java index 328ecba1d..2ebdab545 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java @@ -38,11 +38,20 @@ public class AutoType implements Cloneable, Parcelable { public AutoType() {} + public AutoType(AutoType autoType) { + this.enabled = autoType.enabled; + this.obfuscationOptions = autoType.obfuscationOptions; + this.defaultSequence = autoType.defaultSequence; + for (Map.Entry entry: autoType.windowSeqPairs.entrySet()) { + this.windowSeqPairs.put(entry.getKey(), entry.getValue()); + } + } + public AutoType(Parcel in) { - enabled = in.readByte() != 0; - obfuscationOptions = in.readLong(); - defaultSequence = in.readString(); - windowSeqPairs = MemUtil.readStringParcelableMap(in); + this.enabled = in.readByte() != 0; + this.obfuscationOptions = in.readLong(); + this.defaultSequence = in.readString(); + this.windowSeqPairs = MemUtil.readStringParcelableMap(in); } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java index 53ef5820b..efca3b9ed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java @@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database; import android.os.Parcel; import android.os.Parcelable; - import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.utils.MemUtil; @@ -29,11 +28,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_NOTES; -import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_PASSWORD; -import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_TITLE; -import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_URL; -import static com.kunzisoft.keepass.database.element.PwEntryV4.STR_USERNAME; +import static com.kunzisoft.keepass.database.element.PwEntryV4.*; public class ExtraFields implements Parcelable, Cloneable { @@ -43,6 +38,13 @@ public class ExtraFields implements Parcelable, Cloneable { fields = new HashMap<>(); } + public ExtraFields(ExtraFields extraFields) { + this(); + for (Map.Entry entry: extraFields.fields.entrySet()) { + fields.put(entry.getKey(), new ProtectedString(entry.getValue())); + } + } + public ExtraFields(Parcel in) { fields = MemUtil.readStringParcelableMap(in, ProtectedString.class); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt index c8cd4dc26..b176c071d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyEntryRunnable.kt @@ -39,7 +39,7 @@ class CopyEntryRunnable constructor( override fun nodeAction() { // Update entry with new values mNewParent.touch(false, true) - mEntryCopied = database.copyEntry(mEntryToCopy, mNewParent) + mEntryCopied = database.copyEntryTo(mEntryToCopy, mNewParent) mEntryCopied?.apply { touch(true, true) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java deleted file mode 100644 index e1b9e4aab..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.action.node; - -import android.support.v4.app.FragmentActivity; - -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.GroupVersioned; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -// TODO Kotlinized -public class DeleteGroupRunnable extends ActionNodeDatabaseRunnable { - - private GroupVersioned mGroupToDelete; - private GroupVersioned mParent; - private boolean mRecycle; - - public DeleteGroupRunnable(FragmentActivity context, - Database database, - GroupVersioned group, - AfterActionNodeFinishRunnable finish, - boolean save) { - super(context, database, finish, save); - mGroupToDelete = group; - } - - @Override - public void nodeAction() { - mParent = mGroupToDelete.getParent(); - mParent.touch(false, true); - - // Remove Group from parent - mRecycle = getDatabase().canRecycle(mGroupToDelete); - if (mRecycle) { - getDatabase().recycle(mGroupToDelete); - } - else { - getDatabase().deleteGroup(mGroupToDelete); - } - } - - @NotNull - @Override - public ActionNodeValues nodeFinish(boolean isSuccess, @Nullable String message) { - if ( !isSuccess ) { - if (mRecycle) { - getDatabase().undoRecycle(mGroupToDelete, mParent); - } - else { - // Let's not bother recovering from a failure to save a deleted tree. It is too much work. - // TODO TEST pm.undoDeleteGroupFrom(mGroup, mParent); - } - } - return new ActionNodeValues(isSuccess, message, mGroupToDelete, null); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.kt new file mode 100644 index 000000000..c4058b2c5 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteGroupRunnable.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.action.node + +import android.support.v4.app.FragmentActivity + +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.GroupVersioned + +class DeleteGroupRunnable(context: FragmentActivity, + database: Database, + private val mGroupToDelete: GroupVersioned, + finish: AfterActionNodeFinishRunnable, + save: Boolean) : ActionNodeDatabaseRunnable(context, database, finish, save) { + private var mParent: GroupVersioned? = null + private var mRecycle: Boolean = false + + override fun nodeAction() { + mParent = mGroupToDelete.parent + mParent?.touch(false, true) + + // Remove Group from parent + mRecycle = database.canRecycle(mGroupToDelete) + if (mRecycle) { + database.recycle(mGroupToDelete) + } else { + database.deleteGroup(mGroupToDelete) + } + } + + override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { + if (!isSuccess) { + if (mRecycle) { + mParent?.let { + database.undoRecycle(mGroupToDelete, it) + } + } else { + // Let's not bother recovering from a failure to save a deleted tree. It is too much work. + // TODO database.undoDeleteGroupFrom(mGroup, mParent); + } + } + return ActionNodeValues(isSuccess, message, mGroupToDelete, null) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt index 52f41d843..fd49dd733 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveEntryRunnable.kt @@ -40,7 +40,7 @@ class MoveEntryRunnable constructor( // Move entry in new parent mEntryToMove?.let { mOldParent = it.parent - database.moveEntry(it, mNewParent) + database.moveEntryTo(it, mNewParent) it.touch(true, true) } ?: Log.e(TAG, "Unable to create a copy of the entry") } @@ -50,7 +50,7 @@ class MoveEntryRunnable constructor( // If we fail to save, try to remove in the first place try { if (mEntryToMove != null && mOldParent != null) - database.moveEntry(mEntryToMove, mOldParent!!) + database.moveEntryTo(mEntryToMove, mOldParent!!) } catch (e: Exception) { Log.i(TAG, "Unable to replace the entry") } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt index 4f5a89ffe..fb55c9724 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveGroupRunnable.kt @@ -41,7 +41,7 @@ class MoveGroupRunnable constructor( mOldParent = it.parent // Move group in new parent if not in the current group if (mGroupToMove != mNewParent && !mNewParent.isContainedIn(mGroupToMove)) { - database.moveGroup(mGroupToMove, mNewParent) + database.moveGroupTo(mGroupToMove, mNewParent) mGroupToMove.touch(true, true) finishRun(true) } else { @@ -58,7 +58,7 @@ class MoveGroupRunnable constructor( // If we fail to save, try to move in the first place try { if (mGroupToMove != null && mOldParent != null) - database.moveGroup(mGroupToMove, mOldParent!!) + database.moveGroupTo(mGroupToMove, mOldParent!!) } catch (e: Exception) { Log.i(TAG, "Unable to replace the group") } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt index 944b1bbff..32b2b8142 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt @@ -33,18 +33,21 @@ class UpdateEntryRunnable constructor( : ActionNodeDatabaseRunnable(context, database, finishRunnable, save) { // Keep backup of original values in case save fails - private val mBackupEntry: EntryVersioned = EntryVersioned(mOldEntry) + private var mBackupEntry: EntryVersioned? = null override fun nodeAction() { - // Update entry with new values + mBackupEntry = database.addHistoryBackupTo(mOldEntry) mOldEntry.touch(true, true) + // Update entry with new values mOldEntry.updateWith(mNewEntry) } override fun nodeFinish(isSuccess: Boolean, message: String?): ActionNodeValues { if (!isSuccess) { // If we fail to save, back out changes to global structure - mOldEntry.updateWith(mBackupEntry) + mBackupEntry?.let { + mOldEntry.updateWith(it) + } } return ActionNodeValues(isSuccess, message, mOldEntry, mNewEntry) } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index ed310238a..19a47ad23 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -572,32 +572,27 @@ class Database { } /** - * @return A duplicate entry with the same values, a new UUID, + * @return A duplicate entry with the same values, a new random UUID and a new parent * @param entryToCopy * @param newParent */ - fun copyEntry(entryToCopy: EntryVersioned, newParent: GroupVersioned): EntryVersioned? { - try { - val entryCopied = EntryVersioned(entryToCopy) - entryCopied.nodeId = PwNodeIdUUID() - entryCopied.parent = newParent - addEntryTo(entryCopied, newParent) - return entryCopied - } catch (e: Exception) { - Log.e(TAG, "This version of PwEntry can't be updated", e) - } - - return null + fun copyEntryTo(entryToCopy: EntryVersioned, newParent: GroupVersioned): EntryVersioned? { + val entryCopied = EntryVersioned(entryToCopy) + entryCopied.nodeId = pwDatabaseV3?.newEntryId() ?: pwDatabaseV4?.newEntryId() + entryCopied.parent = newParent + entryCopied.title += " (~)" + addEntryTo(entryCopied, newParent) + return entryCopied } - fun moveEntry(entryToMove: EntryVersioned, newParent: GroupVersioned) { + fun moveEntryTo(entryToMove: EntryVersioned, newParent: GroupVersioned) { entryToMove.parent?.let { removeEntryFrom(entryToMove, it) } addEntryTo(entryToMove, newParent) } - fun moveGroup(groupToMove: GroupVersioned, newParent: GroupVersioned) { + fun moveGroupTo(groupToMove: GroupVersioned, newParent: GroupVersioned) { groupToMove.parent?.let { removeGroupFrom(groupToMove, it) } @@ -674,10 +669,40 @@ class Database { } } - fun createBackupOf(entry: EntryVersioned) { + fun addHistoryBackupTo(entry: EntryVersioned): EntryVersioned { + val backupEntry = EntryVersioned(entry) + + entry.addBackupToHistory() + + // Remove oldest backup if more than max items or max memory pwDatabaseV4?.let { - entry.createBackup(it) + val history = entry.getHistory() + + val maxItems = it.historyMaxItems + if (maxItems >= 0) { + while (history.size > maxItems) { + entry.removeOldestEntryFromHistory() + } + } + + val maxSize = it.historyMaxSize + if (maxSize >= 0) { + while (true) { + var histSize: Long = 0 + for (backup in history) { + histSize += backup.size + } + + if (histSize > maxSize) { + entry.removeOldestEntryFromHistory() + } else { + break + } + } + } } + + return backupEntry } companion object { 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 51cae5b44..8afdc08e8 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 @@ -5,6 +5,7 @@ import android.os.Parcelable import com.kunzisoft.keepass.database.ExtraFields import com.kunzisoft.keepass.database.security.ProtectedString import java.util.* +import kotlin.collections.ArrayList class EntryVersioned : NodeVersioned, PwEntryInterface { @@ -18,19 +19,15 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { this.pwEntryV4?.updateWith(entry.pwEntryV4) } - constructor() - /** - * Use this constructor to copy an Entry + * Use this constructor to copy an Entry with exact same values */ constructor(entry: EntryVersioned) { if (entry.pwEntryV3 != null) { - if (this.pwEntryV3 == null) - this.pwEntryV3 = PwEntryV3() + this.pwEntryV3 = PwEntryV3() } if (entry.pwEntryV4 != null) { - if (this.pwEntryV4 == null) - this.pwEntryV4 = PwEntryV4() + this.pwEntryV4 = PwEntryV4() } updateWith(entry) } @@ -286,8 +283,20 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { pwEntryV4?.stopToManageFieldReferences() } - fun createBackup(db: PwDatabaseV4?) { - pwEntryV4?.createBackup(db) + fun addBackupToHistory() { + pwEntryV4?.let { + val entryHistory = PwEntryV4() + entryHistory.updateWith(it) + it.addEntryToHistory(entryHistory) + } + } + + fun removeOldestEntryFromHistory() { + pwEntryV4?.removeOldestEntryFromHistory() + } + + fun getHistory(): ArrayList { + return pwEntryV4?.history ?: ArrayList() } fun containsCustomData(): Boolean { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index 5d5883141..288953bff 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -124,7 +124,7 @@ public class PwDatabaseV3 extends PwDatabase { public PwNodeIdInt newGroupId() { PwNodeIdInt newId; do { - newId = new PwNodeIdInt(new Random().nextInt()); + newId = new PwNodeIdInt(); } while (isGroupIdUsed(newId)); return newId; @@ -139,7 +139,7 @@ public class PwDatabaseV3 extends PwDatabase { public PwNodeIdUUID newEntryId() { PwNodeIdUUID newId; do { - newId = new PwNodeIdUUID(UUID.randomUUID()); + newId = new PwNodeIdUUID(); } while (isEntryIdUsed(newId)); return newId; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index fd4ab2624..4d0d0e54d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -474,7 +474,7 @@ public class PwDatabaseV4 extends PwDatabase { public PwNodeIdUUID newGroupId() { PwNodeIdUUID newId; do { - newId = new PwNodeIdUUID(UUID.randomUUID()); + newId = new PwNodeIdUUID(); } while (isGroupIdUsed(newId)); return newId; @@ -484,7 +484,7 @@ public class PwDatabaseV4 extends PwDatabase { public PwNodeIdUUID newEntryId() { PwNodeIdUUID newId; do { - newId = new PwNodeIdUUID(UUID.randomUUID()); + newId = new PwNodeIdUUID(); } while (isEntryIdUsed(newId)); return newId; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java index 8b023c869..b7341f9a7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java @@ -79,9 +79,14 @@ public class PwDate implements Cloneable, Parcelable { System.arraycopy(buf, offset, cDate, 0, DATE_SIZE); cDateBuilt = true; } - + + public PwDate(PwDate date) { + jDate = new Date(date.jDate.getTime()); + jDateBuilt = date.jDateBuilt; + } + public PwDate(Date date) { - jDate = date; + jDate = new Date(date.getTime()); jDateBuilt = true; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index 97d5f1737..816340c22 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -92,7 +92,12 @@ public class PwEntryV3 extends PwEntry { return new PwNodeIdUUID(); } - public PwEntryV3() { + @Override + PwNodeId copyNodeId(PwNodeId nodeId) { + return new PwNodeIdUUID(nodeId.getId()); + } + + public PwEntryV3() { super(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index df369158e..b2246b7a4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -69,6 +69,11 @@ public class PwEntryV4 extends PwEntry implements ITimeLog return new PwNodeIdUUID(); } + @Override + PwNodeId copyNodeId(PwNodeId nodeId) { + return new PwNodeIdUUID(nodeId.getId()); + } + public PwEntryV4() { super(); } @@ -127,20 +132,30 @@ public class PwEntryV4 extends PwEntry implements ITimeLog } }; + /** + * Update with deep copy of each entry element + * @param source + */ public void updateWith(PwEntryV4 source) { super.updateWith(source); - customIcon = source.customIcon; + customIcon = new PwIconCustom(source.customIcon); usageCount = source.usageCount; - parentGroupLastMod = source.parentGroupLastMod; + parentGroupLastMod = new PwDate(source.parentGroupLastMod); + // Add all custom elements in map customData.clear(); - customData.putAll(source.customData); // Add all custom elements in map - fields = source.fields; - binaries = source.binaries; + for (Map.Entry entry : source.customData.entrySet()) { + customData.put(entry.getKey(), entry.getValue()); + } + fields = new ExtraFields(source.fields); + for (Map.Entry entry: source.binaries.entrySet()) { + binaries.put(entry.getKey(), new ProtectedBinary(entry.getValue())); + } foregroundColor = source.foregroundColor; backgroupColor = source.backgroupColor; overrideURL = source.overrideURL; - autoType = source.autoType; - history = source.history; + autoType = new AutoType(source.autoType); + history.clear(); + history.addAll(source.history); url = source.url; additional = source.additional; tags = source.tags; @@ -461,47 +476,11 @@ public class PwEntryV4 extends PwEntry implements ITimeLog return size; } - public void createBackup(PwDatabaseV4 db) { - PwEntryV4 copy = new PwEntryV4(); // Clone - copy.history = new ArrayList<>(); - history.add(copy); - - if (db != null) - maintainBackups(db); + public void addEntryToHistory(PwEntryV4 entry) { + history.add(entry); } - private boolean maintainBackups(PwDatabaseV4 db) { - boolean deleted = false; - - int maxItems = db.getHistoryMaxItems(); - if (maxItems >= 0) { - while (history.size() > maxItems) { - removeOldestBackup(); - deleted = true; - } - } - - long maxSize = db.getHistoryMaxSize(); - if (maxSize >= 0) { - while(true) { - long histSize = 0; - for (PwEntryV4 entry : history) { - histSize += entry.getSize(); - } - - if (histSize > maxSize) { - removeOldestBackup(); - deleted = true; - } else { - break; - } - } - } - - return deleted; - } - - private void removeOldestBackup() { + public void removeOldestEntryFromHistory() { Date min = null; int index = -1; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 46d5d1a1d..a9f91825f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -33,6 +33,11 @@ public class PwGroupV3 extends PwGroup { return new PwNodeIdInt(); } + @Override + PwNodeId copyNodeId(PwNodeId nodeId) { + return new PwNodeIdInt(nodeId.getId()); + } + public PwGroupV3() { super(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index cc490ec1e..69d35dd01 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -48,7 +48,12 @@ public class PwGroupV4 extends PwGroup implements IT return new PwNodeIdUUID(); } - public PwGroupV4() { + @Override + PwNodeId copyNodeId(PwNodeId nodeId) { + return new PwNodeIdUUID(nodeId.getId()); + } + + public PwGroupV4() { super(); } @@ -104,10 +109,14 @@ public class PwGroupV4 extends PwGroup implements IT protected void updateWith(PwGroupV4 source) { super.updateWith(source); - customIcon = source.customIcon; + customIcon = new PwIconCustom(source.customIcon); usageCount = source.usageCount; - locationChangeDate = source.locationChangeDate; - customData = source.customData; + locationChangeDate = new PwDate(source.locationChangeDate); + // Add all custom elements in map + customData.clear(); + for (Map.Entry entry : source.customData.entrySet()) { + customData.put(entry.getKey(), entry.getValue()); + } expires = source.expires; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index 03483e307..b77e0c1bf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -44,6 +44,7 @@ public abstract class PwNode private PwDate expireDate = PwDate.PW_NEVER_EXPIRE; abstract PwNodeId initNodeId(); + abstract PwNodeId copyNodeId(PwNodeId nodeId); protected PwNode() {} @@ -76,13 +77,13 @@ public abstract class PwNode } protected void updateWith(PwNode source) { - this.nodeId = source.nodeId; + this.nodeId = copyNodeId(source.nodeId); this.parent = source.parent; this.icon = source.icon; - this.creation = source.creation; - this.lastMod = source.lastMod; - this.lastAccess = source.lastAccess; - this.expireDate = source.expireDate; + this.creation = new PwDate(source.creation); + this.lastMod = new PwDate(source.lastMod); + this.lastAccess = new PwDate(source.lastAccess); + this.expireDate = new PwDate(source.expireDate); } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java index c4782a394..265dea54f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java @@ -21,12 +21,14 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import java.util.Random; + public class PwNodeIdInt extends PwNodeId { private Integer id; public PwNodeIdInt() { - this.id = -1; + this(new Random().nextInt()); } public PwNodeIdInt(int groupId) { @@ -35,6 +37,7 @@ public class PwNodeIdInt extends PwNodeId { } public PwNodeIdInt(Parcel in) { + super(in); id = in.readInt(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java index 6a2edb6d6..6fccfdbc6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java @@ -28,7 +28,7 @@ public class PwNodeIdUUID extends PwNodeId { private UUID uuid; public PwNodeIdUUID() { - this.uuid = UUID.randomUUID(); + this(UUID.randomUUID()); } public PwNodeIdUUID(UUID uuid) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 86922749a..211fbf57e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -790,7 +790,7 @@ public class ImporterV4 extends Importer { return KdbContext.CustomData; } else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { if ( ctxGroup.getNodeId() == null || ctxGroup.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { - ctxGroup.setNodeId(new PwNodeIdUUID()); + ctxGroup.setNodeId(mDatabase.newGroupId()); mDatabase.addGroupIndex(ctxGroup); } @@ -819,7 +819,7 @@ public class ImporterV4 extends Importer { } else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { if ( ctxEntry.getNodeId() == null || ctxEntry.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { - ctxEntry.setNodeId(new PwNodeIdUUID()); + ctxEntry.setNodeId(mDatabase.newEntryId()); mDatabase.addEntryIndex(ctxEntry); } @@ -1048,7 +1048,7 @@ public class ImporterV4 extends Importer { String base64 = ReadString(xpp); if ( base64.length() == 0 ) - return ProtectedBinary.EMPTY; + return new ProtectedBinary(); byte[] data = Base64Coder.decode(base64); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java b/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java index 2f1e373e2..91ba1e415 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java @@ -33,7 +33,6 @@ import java.util.Arrays; public class ProtectedBinary implements Parcelable { private static final String TAG = ProtectedBinary.class.getName(); - public final static ProtectedBinary EMPTY = new ProtectedBinary(); private boolean protect; private byte[] data; @@ -51,13 +50,23 @@ public class ProtectedBinary implements Parcelable { return size; return 0; } - - private ProtectedBinary() { + + /** + * Empty protected binary + */ + public ProtectedBinary() { this.protect = false; this.data = null; this.dataFile = null; this.size = 0; } + + public ProtectedBinary(ProtectedBinary protectedBinary) { + this.protect = protectedBinary.protect; + this.data = protectedBinary.data; + this.dataFile = protectedBinary.dataFile; + this.size = protectedBinary.size; + } public ProtectedBinary(boolean enableProtection, byte[] data) { this.protect = enableProtection; From 0e701189a325895425e34354192e30c2723a92c2 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 11:10:42 +0200 Subject: [PATCH 107/289] Fix search --- .../keepass/database/element/Database.kt | 47 ++++++++----------- .../database/element/GroupVersioned.kt | 2 +- .../keepass/database/element/PwEntryV3.java | 2 +- .../keepass/database/element/PwEntryV4.java | 11 +++-- .../database/element/PwGroupInterface.kt | 6 ++- .../keepass/database/element/PwGroupV3.java | 2 +- .../keepass/database/element/PwGroupV4.java | 23 +++------ .../keepass/database/element/PwNode.java | 4 +- .../database/element/PwNodeInterface.kt | 2 +- .../database/search/EntrySearchHandlerV4.java | 16 +++---- .../keepass/database/search/SearchDbHelper.kt | 39 ++++++++------- .../keepass/settings/PreferencesUtil.java | 6 +++ 12 files changed, 75 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 19a47ad23..26628f9ce 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -37,6 +37,7 @@ import com.kunzisoft.keepass.database.save.PwDbV3Output import com.kunzisoft.keepass.database.save.PwDbV4Output import com.kunzisoft.keepass.database.search.SearchDbHelper import com.kunzisoft.keepass.icons.IconDrawableFactory +import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.stream.LEDataInputStream import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.utils.EmptyUtils @@ -297,7 +298,7 @@ class Database { try { isPasswordEncodingError = !(pwDatabaseV3?.validatePasswordEncoding(password) ?: pwDatabaseV4?.validatePasswordEncoding(password) ?: true) - searchHelper = SearchDbHelper(ctx) + searchHelper = SearchDbHelper(PreferencesUtil.omitBackup(ctx)) loaded = true } catch (e: Exception) { Log.e(TAG, "Load can't be performed with this Database version", e) @@ -313,40 +314,30 @@ class Database { @JvmOverloads fun search(str: String, max: Int = Integer.MAX_VALUE): GroupVersioned? { - return if (searchHelper == null) null else searchHelper?.search(this, str, max) + return searchHelper?.search(this, str, max) } fun searchEntry(query: String): Cursor? { - if (pwDatabaseV3 != null) { - val cursorV3 = EntryCursorV3() - if (query.isNotEmpty()) { - val searchResult = search(query, 6) - if (searchResult != null) { - for (entry in searchResult.getChildEntries()) { - if (!entry.isMetaStream) { // TODO metastream - cursorV3.addEntry(entry.pwEntryV3) - } - } + + var cursorV3: EntryCursorV3? = null + var cursorV4: EntryCursorV4? = null + + if (pwDatabaseV3 != null) + cursorV3 = EntryCursorV3() + if (pwDatabaseV4 != null) + cursorV4 = EntryCursorV4() + + val searchResult = search(query, SearchDbHelper.MAX_SEARCH_ENTRY) + if (searchResult != null) { + for (entry in searchResult.getChildEntries()) { + if (!entry.isMetaStream) { // TODO metastream + cursorV3?.addEntry(entry.pwEntryV3) + cursorV4?.addEntry(entry.pwEntryV4) } } - return cursorV3 } - if (pwDatabaseV4 != null) { - val cursorV4 = EntryCursorV4() - if (query.isNotEmpty()) { - val searchResult = search(query, 6) - if (searchResult != null) { - for (entry in searchResult.getChildEntries()) { - if (!entry.isMetaStream) { // TODO metastream - cursorV4.addEntry(entry.pwEntryV4) - } - } - } - } - return cursorV4 - } - return null + return cursorV3 ?: cursorV4 } fun getEntryFrom(cursor: Cursor): EntryVersioned? { 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 bd51e504f..3b47487c1 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 @@ -114,7 +114,7 @@ class GroupVersioned : NodeVersioned, PwGroupInterface { } @Override - public Boolean isSearchingEnabled() { + public boolean isSearchingEnabled() { return false; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index b2246b7a4..af41ba199 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -85,7 +85,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLog parentGroupLastMod = parcel.readParcelable(PwDate.class.getClassLoader()); customData = MemUtil.readStringParcelableMap(parcel); fields = parcel.readParcelable(ExtraFields.class.getClassLoader()); - binaries = MemUtil.readStringParcelableMap(parcel, ProtectedBinary.class); + // TODO binaries = MemUtil.readStringParcelableMap(parcel, ProtectedBinary.class); foregroundColor = parcel.readString(); backgroupColor = parcel.readString(); overrideURL = parcel.readString(); @@ -313,7 +313,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLog } @Override - public void setIcon(PwIcon icon) { + public void setIcon(@NonNull PwIcon icon) { if (icon instanceof PwIconStandard) setIconStandard((PwIconStandard) icon); if (icon instanceof PwIconCustom) @@ -508,12 +508,13 @@ public class PwEntryV4 extends PwEntry implements ITimeLog public void touchLocation() { parentGroupLastMod = new PwDate(); } - - public Boolean isSearchingEnabled() { + + @Override + public boolean isSearchingEnabled() { if (getParent() != null) { return getParent().isSearchingEnabled(); } - return PwGroupV4.DEFAULT_SEARCHING_ENABLED; + return true; } /** diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt index f5cd9d562..01aa45ee9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupInterface.kt @@ -27,10 +27,12 @@ interface PwGroupInterface, Entry> : PwNod fun doForEachChild(entryHandler: NodeHandler, groupHandler: NodeHandler?): Boolean { for (entry in this.getChildEntries()) { - if (!entryHandler.operate(entry)) return false + if (!entryHandler.operate(entry)) + return false } for (group in this.getChildGroups()) { - if (groupHandler != null && !groupHandler.operate(group)) return false + if (groupHandler != null && !groupHandler.operate(group)) + return false group.doForEachChild(entryHandler, groupHandler) } return true diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index a9f91825f..4b98ff451 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -101,7 +101,7 @@ public class PwGroupV3 extends PwGroup { } @Override - public Boolean isSearchingEnabled() { + public boolean isSearchingEnabled() { return false; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 69d35dd01..207a57e0c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -29,8 +29,6 @@ import java.util.UUID; public class PwGroupV4 extends PwGroup implements ITimeLogger { - public static final boolean DEFAULT_SEARCHING_ENABLED = true; - private PwIconCustom customIcon = PwIconCustom.ZERO; private long usageCount = 0; private PwDate locationChangeDate = new PwDate(); @@ -277,20 +275,13 @@ public class PwGroupV4 extends PwGroup implements IT this.lastTopVisibleEntry = lastTopVisibleEntry; } - public Boolean isSearchingEnabled() { - - GroupVersioned group = new GroupVersioned(this); - while (group != null) { - Boolean search = group.isSearchingEnabled(); - if (search != null) { - return search; - } - group = group.getParent(); - } - - // If we get to the root tree and its null, default to true - return true; - } + @Override + public boolean isSearchingEnabled() { + if (getParent() != null) { + return getParent().isSearchingEnabled(); + } + return true; + } @Override public boolean allowAddEntryIfIsRoot() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index b77e0c1bf..6ac10fb81 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -22,6 +22,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.NonNull; import org.joda.time.LocalDate; /** @@ -119,12 +120,13 @@ public abstract class PwNode /** * @return Visual icon */ + @NonNull public PwIcon getIcon() { return icon; } @Override - public void setIcon(PwIcon icon) { + public void setIcon(@NonNull PwIcon icon) { this.icon = icon; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt index aade02266..c63d1c028 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt @@ -22,7 +22,7 @@ interface PwNodeInterface : SmallTimeInterface, Parcelable { */ var parent: ParentGroup? - val isSearchingEnabled: Boolean? + val isSearchingEnabled: Boolean fun containsParent(): Boolean diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index ec1270a50..7c12fe603 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -67,15 +67,13 @@ public class EntrySearchHandlerV4 extends NodeHandler { PwGroupV4 parent = entry.getParent(); if (parent != null) { String groupName = parent.getTitle(); - if (groupName != null) { - if (sp.ignoreCase) { - groupName = groupName.toLowerCase(); - } + if (sp.ignoreCase) { + groupName = groupName.toLowerCase(); + } - if (groupName.contains(term)) { - listStorage.add(entry); - return true; - } + if (groupName.contains(term)) { + listStorage.add(entry); + return true; } } } @@ -101,7 +99,7 @@ public class EntrySearchHandlerV4 extends NodeHandler { EntrySearchStringIterator iter = new EntrySearchStringIteratorV4(entry, sp); while (iter.hasNext()) { String str = iter.next(); - if (str != null && str.length() > 0) { + if (str.length() > 0) { if (sp.ignoreCase) { str = str.toLowerCase(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt index 1b1fd9204..10779dcc5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -19,9 +19,6 @@ */ package com.kunzisoft.keepass.database.search -import android.content.Context -import android.preference.PreferenceManager -import com.kunzisoft.keepass.R import com.kunzisoft.keepass.database.NodeHandler import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.EntryVersioned @@ -29,42 +26,44 @@ import com.kunzisoft.keepass.database.element.GroupVersioned import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator import java.util.* -class SearchDbHelper(private val mContext: Context) { - private var incrementEntry = 0 +class SearchDbHelper(private val isOmitBackup: Boolean) { - private fun omitBackup(): Boolean { - val prefs = PreferenceManager.getDefaultSharedPreferences(mContext) - return prefs.getBoolean(mContext.getString(R.string.omitbackup_key), mContext.resources.getBoolean(R.bool.omitbackup_default)) + companion object { + const val MAX_SEARCH_ENTRY = 6 } - fun search(pm: Database, qStr: String, max: Int): GroupVersioned { + private var incrementEntry = 0 - val searchGroup = pm.createGroup() - searchGroup!!.title = "\"" + qStr + "\"" + fun search(database: Database, qStr: String, max: Int): GroupVersioned? { + + val searchGroup = database.createGroup() + searchGroup?.title = "\"" + qStr + "\"" // Search all entries val loc = Locale.getDefault() val finalQStr = qStr.toLowerCase(loc) - val isOmitBackup = omitBackup() - incrementEntry = 0 - pm.rootGroup?.doForEachChild( + database.rootGroup?.doForEachChild( object : NodeHandler() { override fun operate(entry: EntryVersioned): Boolean { + if (incrementEntry >= max) + return false if (entryContainsString(entry, finalQStr, loc)) { - searchGroup.addChildEntry(entry) + searchGroup?.addChildEntry(entry) incrementEntry++ } // Stop searching when we have max entries - return incrementEntry <= max + return incrementEntry < max } }, object : NodeHandler() { override fun operate(group: GroupVersioned): Boolean { - return if (pm.isGroupSearchable(group, isOmitBackup)!!) { - true - } else incrementEntry <= max + return when { + incrementEntry >= max -> false + database.isGroupSearchable(group, isOmitBackup) -> true + else -> incrementEntry < max + } } }) diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java index 0493ffe9e..f1426f7d3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java @@ -54,6 +54,12 @@ public class PreferencesUtil { sharedPreferencesEditor.apply(); } + public static boolean omitBackup(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean(context.getString(R.string.omitbackup_key), + context.getResources().getBoolean(R.bool.omitbackup_default)); + } + public static boolean showUsernamesListEntries(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return prefs.getBoolean(context.getString(R.string.list_entries_show_username_key), From 6ba45c3d87c27c133ad7cfd65e90a4853e44b5f3 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 12:34:06 +0200 Subject: [PATCH 108/289] Better search implementation, kotlinize, start remove clone --- .../{StrUtilTest.java => StringUtilTest.java} | 14 +- .../kunzisoft/keepass/compat/PRNGFixes.java | 4 +- .../keepass/crypto/NativeAESCipherSpi.java | 8 +- .../keepass/database/ExtraFields.java | 23 +-- .../keepass/database/element/PwEntryV3.java | 12 +- .../keepass/database/element/PwEntryV4.java | 18 +- .../iterator/EntrySearchStringIterator.kt | 21 +-- .../iterator/EntrySearchStringIteratorV3.java | 78 ++++---- .../iterator/EntrySearchStringIteratorV4.kt | 54 +++--- .../database/search/EntrySearchHandlerV4.java | 44 ++--- .../keepass/database/search/SearchDbHelper.kt | 26 ++- .../database/search/SearchParameters.java | 61 ------ .../database/search/SearchParameters.kt | 69 +++++++ .../database/search/SearchParametersV4.java | 41 ----- .../search/SearchParametersV4.kt} | 41 ++--- .../kunzisoft/keepass/utils/SprEngineV4.java | 174 ++++++++++-------- .../com/kunzisoft/keepass/utils/StrUtil.java | 89 --------- .../com/kunzisoft/keepass/utils/StringUtil.kt | 95 ++++++++++ 18 files changed, 414 insertions(+), 458 deletions(-) rename app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/{StrUtilTest.java => StringUtilTest.java} (71%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.java rename app/src/main/java/com/kunzisoft/keepass/{utils/SprContextV4.java => database/search/SearchParametersV4.kt} (51%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/StrUtil.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StrUtilTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.java similarity index 71% rename from app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StrUtilTest.java rename to app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.java index c6b22ad5f..3ee6130c6 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StrUtilTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.java @@ -21,25 +21,25 @@ package com.kunzisoft.keepass.tests.utils; import java.util.Locale; -import com.kunzisoft.keepass.utils.StrUtil; +import com.kunzisoft.keepass.utils.StringUtil; import junit.framework.TestCase; -public class StrUtilTest extends TestCase { +public class StringUtilTest extends TestCase { private final String text = "AbCdEfGhIj"; private final String search = "BcDe"; private final String badSearch = "Ed"; public void testIndexOfIgnoreCase1() { - assertEquals(1, StrUtil.indexOfIgnoreCase(text, search, Locale.ENGLISH)); + assertEquals(1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH)); } public void testIndexOfIgnoreCase2() { - assertEquals(-1, StrUtil.indexOfIgnoreCase(text, search, Locale.ENGLISH), 2); + assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH), 2); } public void testIndexOfIgnoreCase3() { - assertEquals(-1, StrUtil.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH)); + assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH)); } private final String repText = "AbCtestingaBc"; @@ -48,10 +48,10 @@ public class StrUtilTest extends TestCase { private final String repNew = "12345"; private final String repResult = "12345testing12345"; public void testReplaceAllIgnoresCase1() { - assertEquals(repResult, StrUtil.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH)); + assertEquals(repResult, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH)); } public void testReplaceAllIgnoresCase2() { - assertEquals(repText, StrUtil.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH)); + assertEquals(repText, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH)); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/compat/PRNGFixes.java b/app/src/main/java/com/kunzisoft/keepass/compat/PRNGFixes.java index 4987d1811..f1a2c4e01 100644 --- a/app/src/main/java/com/kunzisoft/keepass/compat/PRNGFixes.java +++ b/app/src/main/java/com/kunzisoft/keepass/compat/PRNGFixes.java @@ -13,7 +13,7 @@ package com.kunzisoft.keepass.compat; import android.os.Build; import android.os.Process; -import com.kunzisoft.keepass.utils.StrUtil; +import com.kunzisoft.keepass.utils.StringUtil; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; @@ -66,7 +66,7 @@ public final class PRNGFixes { private static boolean supportedOnThisDevice() { // Blacklist on samsung devices - if (StrUtil.indexOfIgnoreCase(Build.MANUFACTURER, "samsung", Locale.ENGLISH) >= 0) { + if (StringUtil.INSTANCE.indexOfIgnoreCase(Build.MANUFACTURER, "samsung", Locale.ENGLISH) >= 0) { return false; } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java index 14d12bae1..e24550706 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java @@ -172,7 +172,13 @@ public class NativeAESCipherSpi extends CipherSpi { @Override protected byte[] engineGetIV() { - return mIV.clone(); + byte[] copyIV = new byte[0]; + if (mIV != null) { + int lengthIV = mIV.length; + copyIV = new byte[lengthIV]; + System.arraycopy(mIV, 0, copyIV, 0, lengthIV); + } + return copyIV; } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java index efca3b9ed..184f806f3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java @@ -30,7 +30,7 @@ import java.util.Map; import static com.kunzisoft.keepass.database.element.PwEntryV4.*; -public class ExtraFields implements Parcelable, Cloneable { +public class ExtraFields implements Parcelable { private Map fields; @@ -151,25 +151,4 @@ public class ExtraFields implements Parcelable, Cloneable { && !key.equals(STR_PASSWORD) && !key.equals(STR_URL) && !key.equals(STR_NOTES); } - - @Override - public ExtraFields clone() { - try { - ExtraFields clone = (ExtraFields) super.clone(); - clone.fields = copyMap(this.fields); - return clone; - } catch (CloneNotSupportedException e) { - e.printStackTrace(); - } - return null; - } - - private Map copyMap( - Map original) { - HashMap copy = new HashMap<>(); - for (Map.Entry entry : original.entrySet()) { - copy.put(entry.getKey(), new ProtectedString(entry.getValue())); - } - return copy; - } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index e5425762c..887dca568 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -145,13 +145,17 @@ public class PwEntryV3 extends PwEntry { super.updateWith(source); title = source.title; username = source.username; - int passLen = source.password.length; - password = new byte[passLen]; - System.arraycopy(source.password, 0, password, 0, passLen); + + if (source.password != null) { + int passLen = source.password.length; + password = new byte[passLen]; + System.arraycopy(source.password, 0, password, 0, passLen); + } + url = source.url; additional = source.additional; - binaryDesc = source.binaryDesc; + if ( source.binaryData != null ) { int descLen = source.binaryData.length; binaryData = new byte[descLen]; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index af41ba199..c20f9abce 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -172,7 +172,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLog // newEntry.usageCount stay the same in copy newEntry.parentGroupLastMod = this.parentGroupLastMod.clone(); - newEntry.fields = this.fields.clone(); + newEntry.fields = new ExtraFields(this.fields); // TODO customData make copy from hashmap newEntry.binaries = (HashMap) this.binaries.clone(); // newEntry.foregroundColor stay the same in copy @@ -204,20 +204,22 @@ public class PwEntryV4 extends PwEntry implements ITimeLog this.mDecodeRef = false; } + /** + * Decode a reference key woth the SprEngineV4 + * @param decodeRef + * @param key + * @return + */ private String decodeRefKey(boolean decodeRef, String key) { String text = getProtectedStringValue(key); if (decodeRef) { - text = decodeRef(text, mDatabase); + if (mDatabase == null) + return text; + return new SprEngineV4().compile(text, this, mDatabase); } return text; } - private String decodeRef(String text, PwDatabase db) { - if (db == null) { return text; } - SprEngineV4 spr = new SprEngineV4(); - return spr.compile(text, this, db); - } - @NonNull @Override public String getUsername() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt index c32979c5b..43c6bb9a1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt @@ -19,23 +19,4 @@ */ package com.kunzisoft.keepass.database.iterator -import com.kunzisoft.keepass.database.element.EntryVersioned - -abstract class EntrySearchStringIterator : Iterator { - - companion object { - - fun getInstance(entry: EntryVersioned): EntrySearchStringIterator { - if (entry.pwEntryV3 != null) { - return EntrySearchStringIteratorV3(entry.pwEntryV3) - } - if (entry.pwEntryV4 != null) { - return EntrySearchStringIteratorV4(entry.pwEntryV4!!) - } - - throw RuntimeException("This should not be possible") - } - } - - -} +abstract class EntrySearchStringIterator : Iterator \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java index 2c3465ce7..945865092 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -25,18 +25,17 @@ import com.kunzisoft.keepass.database.search.SearchParameters; import java.util.NoSuchElementException; public class EntrySearchStringIteratorV3 extends EntrySearchStringIterator { - - private PwEntryV3 entry; - private SearchParameters sp; - + + private PwEntryV3 mEntry; + private SearchParameters mSearchParameters; + public EntrySearchStringIteratorV3(PwEntryV3 entry) { - this.entry = entry; - this.sp = SearchParameters.DEFAULT; + this(entry, new SearchParameters()); } - public EntrySearchStringIteratorV3(PwEntryV3 entry, SearchParameters sp) { - this.entry = entry; - this.sp = sp; + public EntrySearchStringIteratorV3(PwEntryV3 entry, SearchParameters searchParameters) { + this.mEntry = entry; + this.mSearchParameters = searchParameters; } private static final int title = 0; @@ -44,76 +43,67 @@ public class EntrySearchStringIteratorV3 extends EntrySearchStringIterator { private static final int username = 2; private static final int comment = 3; private static final int maxEntries = 4; - + private int current = 0; - + @Override public boolean hasNext() { return current < maxEntries; } - + @Override public String next() { // Past the end of the list if (current == maxEntries) { throw new NoSuchElementException("Past final string"); } - + useSearchParameters(); - + String str = getCurrentString(); current++; return str; } - + private void useSearchParameters() { - - if (sp == null) { return; } + + if (mSearchParameters == null) { return; } boolean found = false; - while (!found) { switch (current) { case title: - found = sp.searchInTitles; + found = mSearchParameters.getSearchInTitles(); break; - case url: - found = sp.searchInUrls; + found = mSearchParameters.getSearchInUrls(); break; - case username: - found = sp.searchInUserNames; + found = mSearchParameters.getSearchInUserNames(); break; - case comment: - found = sp.searchInNotes; + found = mSearchParameters.getSearchInNotes(); break; - default: found = true; } - - if (!found) { current++; } + + if (!found) { current++; } } } - + private String getCurrentString() { switch (current) { - case title: - return entry.getTitle(); - - case url: - return entry.getUrl(); - - case username: - return entry.getUsername(); - - case comment: - return entry.getNotes(); - - default: - return ""; + case title: + return mEntry.getTitle(); + case url: + return mEntry.getUrl(); + case username: + return mEntry.getUsername(); + case comment: + return mEntry.getNotes(); + default: + return ""; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt index 06c3fc341..3886040fb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt @@ -27,60 +27,60 @@ import kotlin.collections.Map.Entry class EntrySearchStringIteratorV4 : EntrySearchStringIterator { - private var current: String? = null - private var setIterator: Iterator>? = null - private var sp: SearchParametersV4? = null + private var mCurrent: String? = null + private var mSetIterator: Iterator>? = null + private var mSearchParametersV4: SearchParametersV4? = null constructor(entry: PwEntryV4) { - this.sp = SearchParametersV4.DEFAULT - setIterator = entry.fields.listOfAllFields.entries.iterator() + this.mSearchParametersV4 = SearchParametersV4() + mSetIterator = entry.fields.listOfAllFields.entries.iterator() advance() } - constructor(entry: PwEntryV4, sp: SearchParametersV4) { - this.sp = sp - setIterator = entry.fields.listOfAllFields.entries.iterator() + constructor(entry: PwEntryV4, searchParametersV4: SearchParametersV4) { + this.mSearchParametersV4 = searchParametersV4 + mSetIterator = entry.fields.listOfAllFields.entries.iterator() advance() } override fun hasNext(): Boolean { - return current != null + return mCurrent != null } override fun next(): String { - if (current == null) { + if (mCurrent == null) { throw NoSuchElementException("Past the end of the list.") } - val next = current + val next = mCurrent advance() return next!! } private fun advance() { - while (setIterator!!.hasNext()) { - val entry = setIterator!!.next() + mSetIterator?.let { + while (it.hasNext()) { + val entry = it.next() + val key = entry.key - val key = entry.key - - if (searchInField(key)) { - current = entry.value.toString() - return + if (searchInField(key)) { + mCurrent = entry.value.toString() + return + } } - } - current = null + mCurrent = null } private fun searchInField(key: String): Boolean { - when (key) { - PwEntryV4.STR_TITLE -> return sp!!.searchInTitles - PwEntryV4.STR_USERNAME -> return sp!!.searchInUserNames - PwEntryV4.STR_PASSWORD -> return sp!!.searchInPasswords - PwEntryV4.STR_URL -> return sp!!.searchInUrls - PwEntryV4.STR_NOTES -> return sp!!.searchInNotes - else -> return sp!!.searchInOther + return when (key) { + PwEntryV4.STR_TITLE -> mSearchParametersV4!!.searchInTitles + PwEntryV4.STR_USERNAME -> mSearchParametersV4!!.searchInUserNames + PwEntryV4.STR_PASSWORD -> mSearchParametersV4!!.searchInPasswords + PwEntryV4.STR_URL -> mSearchParametersV4!!.searchInUrls + PwEntryV4.STR_NOTES -> mSearchParametersV4!!.searchInNotes + else -> mSearchParametersV4!!.searchInOther } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index 7c12fe603..a202944ed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -24,7 +24,7 @@ import com.kunzisoft.keepass.database.element.PwEntryV4; import com.kunzisoft.keepass.database.element.PwGroupV4; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4; -import com.kunzisoft.keepass.utils.StrUtil; +import com.kunzisoft.keepass.utils.StringUtil; import com.kunzisoft.keepass.utils.UuidUtil; import java.util.Date; @@ -33,53 +33,53 @@ import java.util.Locale; public class EntrySearchHandlerV4 extends NodeHandler { - private List listStorage; - protected SearchParametersV4 sp; + private List mListStorage; + private SearchParametersV4 mSearchParametersV4; protected Date now; - public EntrySearchHandlerV4(SearchParametersV4 sp, List listStorage) { - this.listStorage = listStorage; - this.sp = sp; + public EntrySearchHandlerV4(SearchParametersV4 searchParametersV4, List listStorage) { + this.mListStorage = listStorage; + this.mSearchParametersV4 = searchParametersV4; this.now = new Date(); } @Override public boolean operate(PwEntryV4 entry) { - if (sp.respectEntrySearchingDisabled && !entry.isSearchingEnabled()) { + if (mSearchParametersV4.getRespectEntrySearchingDisabled() && !entry.isSearchingEnabled()) { return true; } - if (sp.excludeExpired && entry.isExpires() && now.after(entry.getExpiryTime().getDate())) { + if (mSearchParametersV4.getExcludeExpired() && entry.isExpires() && now.after(entry.getExpiryTime().getDate())) { return true; } - String term = sp.searchString; - if (sp.ignoreCase) { + String term = mSearchParametersV4.getSearchString(); + if (mSearchParametersV4.getIgnoreCase()) { term = term.toLowerCase(); } if (searchStrings(entry, term)) { - listStorage.add(entry); + mListStorage.add(entry); return true; } - if (sp.searchInGroupNames) { + if (mSearchParametersV4.getSearchInGroupNames()) { PwGroupV4 parent = entry.getParent(); if (parent != null) { String groupName = parent.getTitle(); - if (sp.ignoreCase) { + if (mSearchParametersV4.getIgnoreCase()) { groupName = groupName.toLowerCase(); } if (groupName.contains(term)) { - listStorage.add(entry); + mListStorage.add(entry); return true; } } } if (searchID(entry)) { - listStorage.add(entry); + mListStorage.add(entry); return true; } @@ -87,20 +87,20 @@ public class EntrySearchHandlerV4 extends NodeHandler { } private boolean searchID(PwEntryV4 entry) { - if (sp.searchInUUIDs) { + if (mSearchParametersV4.getSearchInUUIDs()) { String hex = UuidUtil.toHexString(entry.getNodeId().getId()); - return StrUtil.indexOfIgnoreCase(hex, sp.searchString, Locale.ENGLISH) >= 0; + return StringUtil.INSTANCE.indexOfIgnoreCase(hex, mSearchParametersV4.getSearchString(), Locale.ENGLISH) >= 0; } - + return false; } private boolean searchStrings(PwEntryV4 entry, String term) { - EntrySearchStringIterator iter = new EntrySearchStringIteratorV4(entry, sp); - while (iter.hasNext()) { - String str = iter.next(); + EntrySearchStringIterator iterator = new EntrySearchStringIteratorV4(entry, mSearchParametersV4); + while (iterator.hasNext()) { + String str = iterator.next(); if (str.length() > 0) { - if (sp.ignoreCase) { + if (mSearchParametersV4.getIgnoreCase()) { str = str.toLowerCase(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt index 10779dcc5..422572359 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt @@ -24,6 +24,8 @@ import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.EntryVersioned import com.kunzisoft.keepass.database.element.GroupVersioned import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator +import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV3 +import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4 import java.util.* class SearchDbHelper(private val isOmitBackup: Boolean) { @@ -72,13 +74,23 @@ class SearchDbHelper(private val isOmitBackup: Boolean) { private fun entryContainsString(entry: EntryVersioned, qStr: String, loc: Locale): Boolean { // Search all strings in the entry - val iterator = EntrySearchStringIterator.getInstance(entry) - while (iterator.hasNext()) { - val str = iterator.next() - if (str.isNotEmpty()) { - val lower = str.toLowerCase(loc) - if (lower.contains(qStr)) { - return true + + var iterator: EntrySearchStringIterator? = null + entry.pwEntryV3?.let { + iterator = EntrySearchStringIteratorV3(it) + } + entry.pwEntryV4?.let { + iterator = EntrySearchStringIteratorV4(it) + } + + iterator?.let { + while (it.hasNext()) { + val str = it.next() + if (str.isNotEmpty()) { + val lower = str.toLowerCase(loc) + if (lower.contains(qStr)) { + return true + } } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.java deleted file mode 100644 index 6b671d754..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.search; - -/** - * @author bpellin - * Parameters for searching strings in the database. - * - */ -public class SearchParameters implements Cloneable { - public static final SearchParameters DEFAULT = new SearchParameters(); - - public String searchString; - - public boolean regularExpression = false; - public boolean searchInTitles = true; - public boolean searchInUserNames = true; - public boolean searchInPasswords = false; - public boolean searchInUrls = true; - public boolean searchInGroupNames = false; - public boolean searchInNotes = true; - public boolean ignoreCase = true; - public boolean ignoreExpired = false; - public boolean respectEntrySearchingDisabled = true; - public boolean excludeExpired = false; - - @Override - public Object clone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } - - public void setupNone() { - searchInTitles = false; - searchInUserNames = false; - searchInPasswords = false; - searchInUrls = false; - searchInGroupNames = false; - searchInNotes = false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.kt new file mode 100644 index 000000000..4db7828ae --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParameters.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.search + +/** + * Parameters for searching strings in the database. + */ +open class SearchParameters { + + var searchString: String = "" + + var regularExpression = false + var searchInTitles = true + var searchInUserNames = true + var searchInPasswords = false + var searchInUrls = true + var searchInGroupNames = false + var searchInNotes = true + var ignoreCase = true + var ignoreExpired = false + var respectEntrySearchingDisabled = true + var excludeExpired = false + + constructor() + + /** + * Copy search parameters + * @param source + */ + constructor(source: SearchParameters) { + regularExpression = source.regularExpression + searchInTitles = source.searchInTitles + searchInUserNames = source.searchInUserNames + searchInPasswords = source.searchInPasswords + searchInUrls = source.searchInUrls + searchInGroupNames = source.searchInGroupNames + searchInNotes = source.searchInNotes + ignoreCase = source.ignoreCase + ignoreExpired = source.ignoreExpired + respectEntrySearchingDisabled = source.respectEntrySearchingDisabled + excludeExpired = source.excludeExpired + } + + open fun setupNone() { + searchInTitles = false + searchInUserNames = false + searchInPasswords = false + searchInUrls = false + searchInGroupNames = false + searchInNotes = false + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.java deleted file mode 100644 index b53324a0d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.search; - -public class SearchParametersV4 extends SearchParameters implements Cloneable { - public static SearchParametersV4 DEFAULT = new SearchParametersV4(); - - public boolean searchInOther = true; - public boolean searchInUUIDs = false; - public boolean searchInTags = true; - - @Override - public Object clone() { - return super.clone(); - } - - @Override - public void setupNone() { - super.setupNone(); - searchInOther = false; - searchInUUIDs = false; - searchInTags = false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.kt similarity index 51% rename from app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.kt index b5450adca..740e7e3fc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprContextV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchParametersV4.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,31 +17,26 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.utils; +package com.kunzisoft.keepass.database.search -import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwEntryV4; +class SearchParametersV4 : SearchParameters { -import java.util.HashMap; -import java.util.Map; + var searchInOther = true + var searchInUUIDs = false + var searchInTags = true -public class SprContextV4 implements Cloneable { + constructor() : super() - public PwDatabaseV4 db; - public PwEntryV4 entry; - public Map refsCache = new HashMap<>(); - - public SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) { - this.db = db; - this.entry = entry; - } + constructor(searchParametersV4: SearchParametersV4) : super(searchParametersV4) { + this.searchInOther = searchParametersV4.searchInOther + this.searchInUUIDs = searchParametersV4.searchInUUIDs + this.searchInTags = searchParametersV4.searchInTags + } - @Override - protected Object clone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } + override fun setupNone() { + super.setupNone() + searchInOther = false + searchInUUIDs = false + searchInTags = false + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index a40c2ac38..656e8d166 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -37,47 +37,65 @@ public class SprEngineV4 { public class TargetResult { public PwEntryV4 entry; public char wanted; - + public TargetResult(PwEntryV4 entry, char wanted) { this.entry = entry; this.wanted = wanted; } } + private class SprContextV4 { + + public PwDatabaseV4 db; + public PwEntryV4 entry; + public Map refsCache = new HashMap<>(); + + SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) { + this.db = db; + this.entry = entry; + } + + SprContextV4(SprContextV4 source) { + this.db = source.db; + this.entry = source.entry; + this.refsCache = source.refsCache; + } + } + public String compile(String text, PwEntryV4 entry, PwDatabase database) { SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, entry); - + return compileInternal(text, ctx, 0); } - - private String compileInternal(String text, SprContextV4 ctx, int recursionLevel) { + + private String compileInternal(String text, SprContextV4 sprContextV4, int recursionLevel) { if (text == null) { return ""; } - if (ctx == null) { return ""; } + if (sprContextV4 == null) { return ""; } if (recursionLevel >= MAX_RECURSION_DEPTH) { return ""; } - - return fillRefPlaceholders(text, ctx, recursionLevel); + + return fillRefPlaceholders(text, sprContextV4, recursionLevel); } - - private String fillRefPlaceholders(String text, SprContextV4 ctx, int recursionLevel) { - - if (ctx.db == null) { return text; } - + + private String fillRefPlaceholders(String text, SprContextV4 contextV4, int recursionLevel) { + + if (contextV4.db == null) { return text; } + int offset = 0; for (int i = 0; i < 20; ++i) { - text = fillRefsUsingCache(text, ctx); - - int start = StrUtil.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH); + text = fillRefsUsingCache(text, contextV4); + + int start = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH); if (start < 0) { break; } - int end = StrUtil.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH); + int end = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH); if (end <= start) { break; } - + String fullRef = text.substring(start, end - start + 1); - TargetResult result = findRefTarget(fullRef, ctx); - + TargetResult result = findRefTarget(fullRef, contextV4); + if (result != null) { PwEntryV4 found = result.entry; char wanted = result.wanted; - + if (found != null) { String data; switch (wanted) { @@ -103,92 +121,88 @@ public class SprEngineV4 { offset = start + 1; continue; } - - SprContextV4 subCtx = (SprContextV4) ctx.clone(); + + SprContextV4 subCtx = new SprContextV4(contextV4); subCtx.entry = found; - + String innerContent = compileInternal(data, subCtx, recursionLevel + 1); - addRefsToCache(fullRef, innerContent, ctx); - text = fillRefsUsingCache(text, ctx); + addRefsToCache(fullRef, innerContent, contextV4); + text = fillRefsUsingCache(text, contextV4); } else { offset = start + 1; - continue; } } - + } - + return text; } - public TargetResult findRefTarget(String fullRef, SprContextV4 ctx) { + private TargetResult findRefTarget(String fullRef, SprContextV4 contextV4) { if (fullRef == null) { return null; } - + fullRef = fullRef.toUpperCase(Locale.ENGLISH); - if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) { + if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) { return null; } - + String ref = fullRef.substring(STR_REF_START.length(), fullRef.length() - STR_REF_START.length() - STR_REF_END.length()); if (ref.length() <= 4) { return null; } if (ref.charAt(1) != '@') { return null; } if (ref.charAt(3) != ':') { return null; } - - char scan = Character.MIN_VALUE; - char wanted = Character.MIN_VALUE; - - scan = Character.toUpperCase(ref.charAt(2)); - wanted = Character.toUpperCase(ref.charAt(0)); - - SearchParametersV4 sp = new SearchParametersV4(); - sp.setupNone(); - sp.searchString = ref.substring(4); - if (scan == 'T') { sp.searchInTitles = true; } - else if (scan == 'U') { sp.searchInUserNames = true; } - else if (scan == 'A') { sp.searchInUrls = true; } - else if (scan == 'P') { sp.searchInPasswords = true; } - else if (scan == 'N') { sp.searchInNotes = true; } - else if (scan == 'I') { sp.searchInUUIDs = true; } - else if (scan == 'O') { sp.searchInOther = true; } + char scan = Character.toUpperCase(ref.charAt(2)); + char wanted = Character.toUpperCase(ref.charAt(0)); + + SearchParametersV4 searchParametersV4 = new SearchParametersV4(); + searchParametersV4.setupNone(); + + searchParametersV4.setSearchString(ref.substring(4)); + if (scan == 'T') { searchParametersV4.setSearchInTitles(true); } + else if (scan == 'U') { searchParametersV4.setSearchInUserNames(true); } + else if (scan == 'A') { searchParametersV4.setSearchInUrls(true); } + else if (scan == 'P') { searchParametersV4.setSearchInPasswords(true); } + else if (scan == 'N') { searchParametersV4.setSearchInNotes(true); } + else if (scan == 'I') { searchParametersV4.setSearchInUUIDs(true); } + else if (scan == 'O') { searchParametersV4.setSearchInOther(true); } else { return null; } List list = new ArrayList<>(); // TODO type parameter - searchEntries(ctx.db.getRootGroup(), sp, list); - - if (list.size() > 0) { + searchEntries(contextV4.db.getRootGroup(), searchParametersV4, list); + + if (list.size() > 0) { return new TargetResult(list.get(0), wanted); } - + return null; } - + private void addRefsToCache(String ref, String value, SprContextV4 ctx) { if (ref == null) { return; } if (value == null) { return; } if (ctx == null) { return; } - + if (!ctx.refsCache.containsKey(ref)) { ctx.refsCache.put(ref, value); } } - - private String fillRefsUsingCache(String text, SprContextV4 ctx) { - for (Entry entry : ctx.refsCache.entrySet()) { - text = StrUtil.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH); + + private String fillRefsUsingCache(String text, SprContextV4 sprContextV4) { + for (Entry entry : sprContextV4.refsCache.entrySet()) { + text = StringUtil.INSTANCE.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH); } - + return text; } - private void searchEntries(PwGroupV4 root, SearchParametersV4 sp, List listStorage) { - if (sp == null) { return; } + private void searchEntries(PwGroupV4 root, SearchParametersV4 searchParametersV4, List listStorage) { + if (searchParametersV4 == null) { return; } if (listStorage == null) { return; } - List terms = StrUtil.splitSearchTerms(sp.searchString); - if (terms.size() <= 1 || sp.regularExpression) { - root.doForEachChild(new EntrySearchHandlerV4(sp, listStorage), null); + List terms = StringUtil.INSTANCE.splitStringTerms(searchParametersV4.getSearchString()); + if (terms.size() <= 1 || searchParametersV4.getRegularExpression()) { + root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, listStorage), null); return; } @@ -196,41 +210,41 @@ public class SprEngineV4 { Comparator stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length(); Collections.sort(terms, stringLengthComparator); - String fullSearch = sp.searchString; - List pg = root.getChildEntries(); + String fullSearch = searchParametersV4.getSearchString(); + List childEntries = root.getChildEntries(); for (int i = 0; i < terms.size(); i ++) { List pgNew = new ArrayList<>(); - sp.searchString = terms.get(i); + searchParametersV4.setSearchString(terms.get(i)); boolean negate = false; - if (sp.searchString.startsWith("-")) { - sp.searchString = sp.searchString.substring(1); - negate = sp.searchString.length() > 0; + if (searchParametersV4.getSearchString().startsWith("-")) { + searchParametersV4.setSearchString(searchParametersV4.getSearchString().substring(1)); + negate = searchParametersV4.getSearchString().length() > 0; } - if (!root.doForEachChild(new EntrySearchHandlerV4(sp, pgNew), null)) { - pg = null; + if (!root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, pgNew), null)) { + childEntries = null; break; } List complement = new ArrayList<>(); if (negate) { - for (PwEntryV4 entry: pg) { + for (PwEntryV4 entry: childEntries) { if (!pgNew.contains(entry)) { complement.add(entry); } } - pg = complement; + childEntries = complement; } else { - pg = pgNew; + childEntries = pgNew; } } - if (pg != null) { - listStorage.addAll(pg); + if (childEntries != null) { + listStorage.addAll(childEntries); } - sp.searchString = fullSearch; + searchParametersV4.setSearchString(fullSearch); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/StrUtil.java b/app/src/main/java/com/kunzisoft/keepass/utils/StrUtil.java deleted file mode 100644 index 8d00133d0..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/utils/StrUtil.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.utils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public class StrUtil { - public static List splitSearchTerms(String search) { - List list = new ArrayList(); - if (search == null) { return list; } - - StringBuilder sb = new StringBuilder(); - boolean quoted = false; - - for (int i = 0; i < search.length(); i++) { - char ch = search.charAt(i); - - if ( ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n')) - && !quoted) { - - int len = sb.length(); - if (len > 0) { - list.add(sb.toString()); - sb.delete(0, len); - } - else if (ch == '\"') { - quoted = !quoted; - } - else { - sb.append(ch); - } - } - } - - if (sb.length() > 0) { - list.add(sb.toString()); - } - - return list; - } - - public static int indexOfIgnoreCase(String text, String search, int start, Locale locale) { - if (text == null || search == null) return -1; - - return text.toLowerCase(locale).indexOf(search.toLowerCase(locale), start); - } - - public static int indexOfIgnoreCase(String text, String search, Locale locale) { - return indexOfIgnoreCase(text, search, 0, locale); - } - - public static String replaceAllIgnoresCase(String text, String find, String newText, Locale locale) { - if (text == null || find == null || newText == null) { return text; } - - int pos = 0; - while (pos < text.length()) { - pos = indexOfIgnoreCase(text, find, pos, locale); - if (pos < 0) { break; } - - String before = text.substring(0, pos); - String after = text.substring(pos + find.length()); - - text = before.concat(newText).concat(after); - pos += newText.length(); - } - - return text; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt new file mode 100644 index 000000000..26763f011 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/utils/StringUtil.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.utils + +import java.util.ArrayList +import java.util.Locale + +object StringUtil { + + /** + * Create a list of String by split text when ' ', '\t', '\r' or '\n' is found + */ + fun splitStringTerms(text: String?): List { + val list = ArrayList() + if (text == null) { + return list + } + + val stringBuilder = StringBuilder() + var quoted = false + + for (i in 0 until text.length) { + val ch = text[i] + + if ((ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') && !quoted) { + + val len = stringBuilder.length + when { + len > 0 -> { + list.add(stringBuilder.toString()) + stringBuilder.delete(0, len) + } + ch == '\"' -> quoted = !quoted + else -> stringBuilder.append(ch) + } + } + } + + if (stringBuilder.isNotEmpty()) { + list.add(stringBuilder.toString()) + } + + return list + } + + fun indexOfIgnoreCase(text: String, search: String, start: Int, locale: Locale): Int { + return text.toLowerCase(locale).indexOf(search.toLowerCase(locale), start) + + } + + fun indexOfIgnoreCase(text: String, search: String, locale: Locale): Int { + return indexOfIgnoreCase(text, search, 0, locale) + } + + fun replaceAllIgnoresCase(text: String?, find: String?, newText: String?, locale: Locale): String? { + var currentText = text + if (currentText == null || find == null || newText == null) { + return currentText + } + + var pos = 0 + while (pos < currentText!!.length) { + pos = indexOfIgnoreCase(currentText, find, pos, locale) + if (pos < 0) { + break + } + + val before = currentText.substring(0, pos) + val after = currentText.substring(pos + find.length) + + currentText = before + newText + after + pos += newText.length + } + + return currentText + } + +} From e649be230e2210c664459f8883ac07f31f106e69 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 12:58:28 +0200 Subject: [PATCH 109/289] Remove clone --- .../kunzisoft/keepass/tests/PwDateTest.java | 2 +- .../kunzisoft/keepass/database/AutoType.java | 15 +------ .../keepass/database/element/PwDate.java | 41 +++++++------------ .../keepass/database/element/PwEntryV3.java | 28 ------------- .../keepass/database/element/PwEntryV4.java | 27 ------------ .../keepass/database/element/PwGroupV3.java | 10 ----- .../keepass/database/element/PwGroupV4.java | 24 ----------- .../keepass/database/element/PwIcon.java | 5 +-- .../database/element/PwIconCustom.java | 5 --- .../database/element/PwIconStandard.java | 5 --- .../keepass/database/element/PwNode.java | 18 -------- .../keepass/database/element/PwNodeId.java | 7 +--- .../keepass/database/element/PwNodeIdInt.java | 9 ++-- .../database/element/PwNodeIdUUID.java | 11 +++-- 14 files changed, 28 insertions(+), 179 deletions(-) diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java index 16363e0f4..dacde9f56 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java @@ -27,7 +27,7 @@ public class PwDateTest extends TestCase { public void testDate() { PwDate jDate = new PwDate(System.currentTimeMillis()); - PwDate intermediate = jDate.clone(); + PwDate intermediate = new PwDate(jDate); PwDate cDate = new PwDate(intermediate.getCDate(), 0); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java b/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java index 2ebdab545..2e8fb380f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java @@ -28,7 +28,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -public class AutoType implements Cloneable, Parcelable { +public class AutoType implements Parcelable { private static final long OBF_OPT_NONE = 0; public boolean enabled = true; @@ -79,18 +79,6 @@ public class AutoType implements Cloneable, Parcelable { } }; - @SuppressWarnings("unchecked") - public AutoType clone() { - AutoType auto; - try { - auto = (AutoType) super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - auto.windowSeqPairs = (HashMap) windowSeqPairs.clone(); - return auto; - } - public void put(String key, String value) { windowSeqPairs.put(key, value); } @@ -98,5 +86,4 @@ public class AutoType implements Cloneable, Parcelable { public Set> entrySet() { return windowSeqPairs.entrySet(); } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java index b7341f9a7..3fb4d2029 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java @@ -35,13 +35,13 @@ import java.util.Date; * @author bpellin * */ -public class PwDate implements Cloneable, Parcelable { +public class PwDate implements Parcelable { private static final int DATE_SIZE = 5; - private Date jDate; + private Date jDate = null; private boolean jDateBuilt = false; - transient private byte[] cDate; + transient private byte[] cDate = null; transient private boolean cDateBuilt = false; public static final Date NEVER_EXPIRE = getNeverExpire(); @@ -80,9 +80,18 @@ public class PwDate implements Cloneable, Parcelable { cDateBuilt = true; } - public PwDate(PwDate date) { - jDate = new Date(date.jDate.getTime()); - jDateBuilt = date.jDateBuilt; + public PwDate(PwDate source) { + if (source.jDate != null) { + this.jDate = new Date(source.jDate.getTime()); + } + this.jDateBuilt = source.jDateBuilt; + + if (source.cDate != null) { + int dateLength = source.cDate.length; + this.cDate = new byte[dateLength]; + System.arraycopy(source.cDate, 0, this.cDate, 0, dateLength); + } + this.cDateBuilt = source.cDateBuilt; } public PwDate(Date date) { @@ -128,25 +137,6 @@ public class PwDate implements Cloneable, Parcelable { return new PwDate[size]; } }; - - @Override - public PwDate clone() { - PwDate copy = new PwDate(); - - if ( cDateBuilt ) { - byte[] newC = new byte[DATE_SIZE]; - System.arraycopy(cDate, 0, newC, 0, DATE_SIZE); - copy.cDate = newC; - copy.cDateBuilt = true; - } - - if ( jDateBuilt ) { - copy.jDate = (Date) jDate.clone(); - copy.jDateBuilt = true; - } - - return copy; - } public Date getDate() { if ( ! jDateBuilt ) { @@ -273,5 +263,4 @@ public class PwDate implements Cloneable, Parcelable { (cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)); } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index 887dca568..bbae0eef1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -163,34 +163,6 @@ public class PwEntryV3 extends PwEntry { } } - @Override - public PwEntryV3 clone() { - // Attributes in parent - PwEntryV3 newEntry = (PwEntryV3) super.clone(); - - // Attributes here - // newEntry.parent stay the same in copy - // newEntry.groupId stay the same in copy - // newEntry.title stay the same in copy - // newEntry.username stay the same in copy - if (password != null) { - int passLen = password.length; - password = new byte[passLen]; - System.arraycopy(password, 0, newEntry.password, 0, passLen); - } - // newEntry.url stay the same in copy - // newEntry.additional stay the same in copy - - // newEntry.binaryDesc stay the same in copy - if ( binaryData != null ) { - int descLen = binaryData.length; - newEntry.binaryData = new byte[descLen]; - System.arraycopy(binaryData, 0, newEntry.binaryData, 0, descLen); - } - - return newEntry; - } - @Override public Type getType() { return Type.ENTRY; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index c20f9abce..36d7d021e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -161,33 +161,6 @@ public class PwEntryV4 extends PwEntry implements ITimeLog tags = source.tags; } - @SuppressWarnings("unchecked") - @Override - public PwEntryV4 clone() { - // Attributes in parent - PwEntryV4 newEntry = (PwEntryV4) super.clone(); - - // Attributes here - newEntry.customIcon = new PwIconCustom(this.customIcon); - // newEntry.usageCount stay the same in copy - newEntry.parentGroupLastMod = this.parentGroupLastMod.clone(); - - newEntry.fields = new ExtraFields(this.fields); - // TODO customData make copy from hashmap - newEntry.binaries = (HashMap) this.binaries.clone(); - // newEntry.foregroundColor stay the same in copy - // newEntry.backgroupColor stay the same in copy - // newEntry.overrideURL stay the same in copy - newEntry.autoType = autoType.clone(); - newEntry.history = (ArrayList) this.history.clone(); - - // newEntry.url stay the same in copy - // newEntry.additional stay the same in copy - // newEntry.tags stay the same in copy - - return newEntry; - } - @NonNull @Override public Type getType() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index 4b98ff451..d07254e3f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -78,16 +78,6 @@ public class PwGroupV3 extends PwGroup { flags = source.flags; } - @SuppressWarnings("unchecked") - @Override - public PwGroupV3 clone() { - // name is clone automatically (IMMUTABLE) - // newGroup.groupId stay the same in copy - // newGroup.level stay the same in copy - // newGroup.flags stay the same in copy - return (PwGroupV3) super.clone(); - } - @Override public Type getType() { return Type.GROUP; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 207a57e0c..7528e534c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -126,30 +126,6 @@ public class PwGroupV4 extends PwGroup implements IT lastTopVisibleEntry = source.lastTopVisibleEntry; } - @Override - public PwGroupV4 clone() { - // Attributes in parent - PwGroupV4 newGroup = (PwGroupV4) super.clone(); - - // Attributes here - // name is clone automatically (IMMUTABLE) - newGroup.customIcon = new PwIconCustom(this.customIcon); - // newGroup.usageCount stay the same in copy - newGroup.locationChangeDate = this.locationChangeDate.clone(); - // TODO customData make copy from hashmap newGroup.customData = (HashMap) this.customData.clone(); - - // newGroup.expires stay the same in copy - - // newGroup.notes stay the same in copy - // newGroup.isExpanded stay the same in copy - // newGroup.defaultAutoTypeSequence stay the same in copy - // newGroup.enableAutoType stay the same in copy - // newGroup.enableSearching stay the same in copy - // newGroup.lastTopVisibleEntry stay the same in copy - - return newGroup; - } - @NonNull @Override public Type getType() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java index 1430a713a..457da4311 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; -public abstract class PwIcon implements Parcelable, Cloneable { +public abstract class PwIcon implements Parcelable { public static final int UNKNOWN = -1; @@ -38,9 +38,6 @@ public abstract class PwIcon implements Parcelable, Cloneable { public abstract int getIconId(); - @Override - protected abstract PwIcon clone() throws CloneNotSupportedException; - @Override public int describeContents() { return 0; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java index 409e61bed..06c4c76cb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java @@ -88,11 +88,6 @@ public class PwIconCustom extends PwIcon { } }; - @Override - protected PwIconCustom clone() throws CloneNotSupportedException { - return new PwIconCustom(this); - } - @Override public int hashCode() { final int prime = 31; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java index ae4363210..7f0aad21d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java @@ -76,11 +76,6 @@ public class PwIconStandard extends PwIcon { return iconId == 0; } - @Override - protected PwIconStandard clone() throws CloneNotSupportedException { - return new PwIconStandard(this); - } - @Override public int hashCode() { final int prime = 31; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java index 6ac10fb81..5af7ad646 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java @@ -87,24 +87,6 @@ public abstract class PwNode this.expireDate = new PwDate(source.expireDate); } - @Override - public PwNode clone() { - PwNode newNode; - try { - newNode = (PwNode) super.clone(); - newNode.nodeId = nodeId.clone(); - // newNode.parent stay the same in copy - newNode.icon = icon.clone(); - newNode.creation = creation.clone(); - newNode.lastMod = lastMod.clone(); - newNode.lastAccess = lastAccess.clone(); - newNode.expireDate = expireDate.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Clone should be supported"); - } - return newNode; - } - public IdType getId() { return getNodeId().getId(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java index 46f74819d..8891905bb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.os.Parcelable; -public abstract class PwNodeId implements Cloneable, Parcelable { +public abstract class PwNodeId implements Parcelable { public PwNodeId() {} @@ -37,9 +37,4 @@ public abstract class PwNodeId implements Cloneable, Parcelable { } public abstract Id getId(); - - @Override - public PwNodeId clone() throws CloneNotSupportedException { - return (PwNodeId) super.clone(); - } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java index 265dea54f..49fd78211 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java @@ -31,6 +31,10 @@ public class PwNodeIdInt extends PwNodeId { this(new Random().nextInt()); } + public PwNodeIdInt(PwNodeIdInt source) { + this(source.id); + } + public PwNodeIdInt(int groupId) { super(); this.id = groupId; @@ -59,11 +63,6 @@ public class PwNodeIdInt extends PwNodeId { } }; - @Override - public PwNodeIdInt clone() throws CloneNotSupportedException { - return (PwNodeIdInt) super.clone(); - } - @Override public boolean equals(Object compare) { if ( ! (compare instanceof PwNodeIdInt) ) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java index 6fccfdbc6..d67a30e43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java @@ -30,7 +30,11 @@ public class PwNodeIdUUID extends PwNodeId { public PwNodeIdUUID() { this(UUID.randomUUID()); } - + + public PwNodeIdUUID(PwNodeIdUUID source) { + this(source.uuid); + } + public PwNodeIdUUID(UUID uuid) { super(); this.uuid = uuid; @@ -59,11 +63,6 @@ public class PwNodeIdUUID extends PwNodeId { } }; - @Override - public PwNodeIdUUID clone() throws CloneNotSupportedException { - return (PwNodeIdUUID) super.clone(); - } - @Override public boolean equals(Object id) { if ( ! (id instanceof PwNodeIdUUID) ) { From 149b6fbcd47db2722a179a7dd72947ffea60d180 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 13:12:51 +0200 Subject: [PATCH 110/289] Kotlinized PwNodeId --- .../element/{PwNodeId.java => PwNodeId.kt} | 22 ++--- .../keepass/database/element/PwNodeIdInt.java | 88 ------------------- .../keepass/database/element/PwNodeIdInt.kt | 75 ++++++++++++++++ .../database/element/PwNodeIdUUID.java | 88 ------------------- .../keepass/database/element/PwNodeIdUUID.kt | 75 ++++++++++++++++ 5 files changed, 158 insertions(+), 190 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/database/element/{PwNodeId.java => PwNodeId.kt} (65%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.kt similarity index 65% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.kt index 8891905bb..9cfa914ec 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeId.kt @@ -17,24 +17,18 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.element; +package com.kunzisoft.keepass.database.element -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcel +import android.os.Parcelable -public abstract class PwNodeId implements Parcelable { +abstract class PwNodeId : Parcelable { - public PwNodeId() {} + abstract val id: Id - public PwNodeId(Parcel in) {} + override fun writeToParcel(dest: Parcel, flags: Int) {} - @Override - public void writeToParcel(Parcel dest, int flags) {} - - @Override - public int describeContents() { - return 0; + override fun describeContents(): Int { + return 0 } - - public abstract Id getId(); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java deleted file mode 100644 index 49fd78211..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -import java.util.Random; - -public class PwNodeIdInt extends PwNodeId { - - private Integer id; - - public PwNodeIdInt() { - this(new Random().nextInt()); - } - - public PwNodeIdInt(PwNodeIdInt source) { - this(source.id); - } - - public PwNodeIdInt(int groupId) { - super(); - this.id = groupId; - } - - public PwNodeIdInt(Parcel in) { - super(in); - id = in.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(id); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwNodeIdInt createFromParcel(Parcel in) { - return new PwNodeIdInt(in); - } - - @Override - public PwNodeIdInt[] newArray(int size) { - return new PwNodeIdInt[size]; - } - }; - - @Override - public boolean equals(Object compare) { - if ( ! (compare instanceof PwNodeIdInt) ) { - return false; - } - PwNodeIdInt cmp = (PwNodeIdInt) compare; - return id.equals(cmp.id); - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public String toString() { - return id.toString(); - } - - public Integer getId() { - return id; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt new file mode 100644 index 000000000..3b1b1d2ce --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import java.util.Random + +class PwNodeIdInt : PwNodeId { + + override var id: Int = -1 + private set + + constructor(source: PwNodeIdInt) : this(source.id) + + @JvmOverloads + constructor(groupId: Int = Random().nextInt()) : super() { + this.id = groupId + } + + constructor(parcel: Parcel) { + id = parcel.readInt() + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeInt(id) + } + + override fun equals(other: Any?): Boolean { + if (other !is PwNodeIdInt) { + return false + } + return id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + override fun toString(): String { + return id.toString() + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PwNodeIdInt { + return PwNodeIdInt(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java deleted file mode 100644 index d67a30e43..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -import java.util.UUID; - -public class PwNodeIdUUID extends PwNodeId { - - private UUID uuid; - - public PwNodeIdUUID() { - this(UUID.randomUUID()); - } - - public PwNodeIdUUID(PwNodeIdUUID source) { - this(source.uuid); - } - - public PwNodeIdUUID(UUID uuid) { - super(); - this.uuid = uuid; - } - - public PwNodeIdUUID(Parcel in) { - super(in); - uuid = (UUID) in.readSerializable(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeSerializable(uuid); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwNodeIdUUID createFromParcel(Parcel in) { - return new PwNodeIdUUID(in); - } - - @Override - public PwNodeIdUUID[] newArray(int size) { - return new PwNodeIdUUID[size]; - } - }; - - @Override - public boolean equals(Object id) { - if ( ! (id instanceof PwNodeIdUUID) ) { - return false; - } - PwNodeIdUUID v4 = (PwNodeIdUUID) id; - return uuid.equals(v4.uuid); - } - - @Override - public int hashCode() { - return uuid.hashCode(); - } - - @Override - public String toString() { - return uuid.toString(); - } - - public UUID getId() { - return uuid; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt new file mode 100644 index 000000000..f9e711969 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import java.util.UUID + +class PwNodeIdUUID : PwNodeId { + + override var id: UUID = UUID.randomUUID() + private set + + constructor(source: PwNodeIdUUID) : this(source.id) + + @JvmOverloads + constructor(uuid: UUID = UUID.randomUUID()) : super() { + this.id = uuid + } + + constructor(parcel: Parcel) { + id = parcel.readSerializable() as UUID + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeSerializable(id) + } + + override fun equals(other: Any?): Boolean { + if (other !is PwNodeIdUUID) { + return false + } + return this.id == other.id + } + + override fun hashCode(): Int { + return id.hashCode() + } + + override fun toString(): String { + return id.toString() + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(`in`: Parcel): PwNodeIdUUID { + return PwNodeIdUUID(`in`) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} From 47fce14d16471e7bd523d11ae8847f7b00d18314 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 13:47:44 +0200 Subject: [PATCH 111/289] Kotlinized PwIcon --- .../database/cursor/EntryCursorV4.java | 4 +- .../keepass/database/element/PwEntryV4.java | 4 +- .../keepass/database/element/PwGroupV4.java | 4 +- .../element/{PwIcon.java => PwIcon.kt} | 32 ++--- .../database/element/PwIconCustom.java | 112 ------------------ .../keepass/database/element/PwIconCustom.kt | 99 ++++++++++++++++ .../database/element/PwIconFactory.java | 82 ------------- .../keepass/database/element/PwIconFactory.kt | 77 ++++++++++++ .../database/element/PwIconStandard.java | 100 ---------------- .../database/element/PwIconStandard.kt | 89 ++++++++++++++ .../keepass/database/element/PwNodeIdInt.kt | 4 + .../keepass/database/element/PwNodeIdUUID.kt | 8 +- .../element/{PwVersion.java => PwVersion.kt} | 20 ++-- .../keepass/database/save/PwDbV4Output.java | 10 +- .../keepass/icons/IconDrawableFactory.java | 9 +- 15 files changed, 310 insertions(+), 344 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/database/element/{PwIcon.java => PwIcon.kt} (58%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt rename app/src/main/java/com/kunzisoft/keepass/database/element/{PwVersion.java => PwVersion.kt} (67%) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java index 7f56e262f..e1a71ca40 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java @@ -21,8 +21,8 @@ public class EntryCursorV4 extends EntryCursor { entry.getNodeId().getId().getLeastSignificantBits(), entry.getTitle(), entry.getIcon().getIconId(), - entry.getIconCustom().getUUID().getMostSignificantBits(), - entry.getIconCustom().getUUID().getLeastSignificantBits(), + entry.getIconCustom().getUuid().getMostSignificantBits(), + entry.getIconCustom().getUuid().getLeastSignificantBits(), entry.getUsername(), entry.getPassword(), entry.getUrl(), diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index 36d7d021e..ca025125f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -49,7 +49,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLog private transient PwDatabaseV4 mDatabase = null; private transient boolean mDecodeRef = false; - private PwIconCustom customIcon = PwIconCustom.ZERO; + private PwIconCustom customIcon = PwIconCustom.Companion.getZERO(); private long usageCount = 0; private PwDate parentGroupLastMod = new PwDate(); private Map customData = new HashMap<>(); @@ -309,7 +309,7 @@ public class PwEntryV4 extends PwEntry implements ITimeLog public void setIconStandard(PwIconStandard icon) { super.setIcon(icon); - this.customIcon = PwIconCustom.ZERO; + this.customIcon = PwIconCustom.Companion.getZERO(); } public boolean allowExtraFields() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 7528e534c..58b5829c1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -29,7 +29,7 @@ import java.util.UUID; public class PwGroupV4 extends PwGroup implements ITimeLogger { - private PwIconCustom customIcon = PwIconCustom.ZERO; + private PwIconCustom customIcon = PwIconCustom.Companion.getZERO(); private long usageCount = 0; private PwDate locationChangeDate = new PwDate(); private Map customData = new HashMap<>(); @@ -192,7 +192,7 @@ public class PwGroupV4 extends PwGroup implements IT public void setIconStandard(PwIconStandard icon) { // TODO Encapsulate with PwEntryV4 super.setIcon(icon); - this.customIcon = PwIconCustom.ZERO; + this.customIcon = PwIconCustom.Companion.getZERO(); } public void putCustomData(String key, String value) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.kt similarity index 58% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.kt index 457da4311..2ae538656 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,29 +17,23 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.element; +package com.kunzisoft.keepass.database.element -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Parcelable -public abstract class PwIcon implements Parcelable { +abstract class PwIcon protected constructor() : Parcelable { - public static final int UNKNOWN = -1; + abstract val isMetaStreamIcon: Boolean - public boolean isMetaStreamIcon() { - return false; - } + abstract val isUnknown: Boolean - protected PwIcon() {} + abstract val iconId: Int - protected PwIcon(Parcel src) {} + override fun describeContents(): Int { + return 0 + } - public abstract boolean isUnknown(); - - public abstract int getIconId(); - - @Override - public int describeContents() { - return 0; - } + companion object { + const val UNKNOWN = -1 + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java deleted file mode 100644 index 06c4c76cb..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -import java.util.UUID; - -public class PwIconCustom extends PwIcon { - public static final PwIconCustom ZERO = new PwIconCustom(PwDatabase.UUID_ZERO, new byte[0]); - - private final UUID uuid; - transient private byte[] imageData; - - public PwIconCustom(UUID uuid, byte[] data) { - super(); - this.uuid = uuid; - this.imageData = data; - } - - public PwIconCustom(PwIconCustom icon) { - super(); - uuid = icon.uuid; - imageData = icon.imageData; - } - - protected PwIconCustom(Parcel in) { - super(in); - uuid = (UUID) in.readSerializable(); - // TODO Take too much memories - // in.readByteArray(imageData); - } - - @Override - public boolean isUnknown() { - return uuid == null || this.equals(ZERO); - } - - @Override - public int getIconId() { - return UNKNOWN; - } - - public UUID getUUID() { - return uuid; - } - - public byte[] getImageData() { - return imageData; - } - - public void setImageData(byte[] imageData) { - this.imageData = imageData; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeSerializable(uuid); - // Too big for a parcelable dest.writeByteArray(imageData); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwIconCustom createFromParcel(Parcel in) { - return new PwIconCustom(in); - } - - @Override - public PwIconCustom[] newArray(int size) { - return new PwIconCustom[size]; - } - }; - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((uuid == null) ? 0 : uuid.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PwIconCustom other = (PwIconCustom) obj; - if (uuid == null) { - return other.uuid == null; - } else return uuid.equals(other.uuid); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt new file mode 100644 index 000000000..0b949ea90 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2019 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import java.util.UUID + +class PwIconCustom : PwIcon { + + val uuid: UUID + @Transient + var imageData: ByteArray = ByteArray(0) + + override val isMetaStreamIcon: Boolean + get() = false + + override val isUnknown: Boolean + get() = this == ZERO + + override val iconId: Int + get() = PwIcon.UNKNOWN + + constructor(uuid: UUID, data: ByteArray) : super() { + this.uuid = uuid + this.imageData = data + } + + constructor(uuid: UUID) : super() { + this.uuid = uuid + this.imageData = ByteArray(0) + } + + constructor(icon: PwIconCustom) : super() { + uuid = icon.uuid + imageData = icon.imageData + } + + constructor(parcel: Parcel) { + uuid = parcel.readSerializable() as UUID + // TODO Take too much memories + // in.readByteArray(imageData); + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeSerializable(uuid) + // Too big for a parcelable dest.writeByteArray(imageData); + } + + override fun hashCode(): Int { + val prime = 31 + var result = 1 + result = prime * result + uuid.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + if (this === other) + return true + if (other == null) + return false + if (other !is PwIconCustom) + return false + return uuid == other.uuid + } + + companion object { + val ZERO = PwIconCustom(PwDatabase.UUID_ZERO, ByteArray(0)) + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PwIconCustom { + return PwIconCustom(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.java deleted file mode 100644 index 79ea5011e..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; - -import java.util.UUID; - -public class PwIconFactory { - /** customIconMap - * Cache for icon drawable. - * Keys: Integer, Values: PwIconStandard - */ - private ReferenceMap cache = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); - - /** standardIconMap - * Cache for icon drawable. - * Keys: UUID, Values: PwIconCustom - */ - private ReferenceMap customCache = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); - - public PwIconStandard getUnknownIcon() { - return getIcon(PwIconStandard.UNKNOWN); - } - - public PwIconStandard getKeyIcon() { - return getIcon(PwIconStandard.KEY); - } - - public PwIconStandard getTrashIcon() { - return getIcon(PwIconStandard.TRASH); - } - - public PwIconStandard getFolderIcon() { - return getIcon(PwIconStandard.FOLDER); - } - - public PwIconStandard getIcon(int iconId) { - PwIconStandard icon = (PwIconStandard) cache.get(iconId); - - if (icon == null) { - icon = new PwIconStandard(iconId); - cache.put(iconId, icon); - } - - return icon; - } - - public PwIconCustom getIcon(UUID iconUuid) { - PwIconCustom icon = (PwIconCustom) customCache.get(iconUuid); - - if (icon == null) { - icon = new PwIconCustom(iconUuid, null); - customCache.put(iconUuid, icon); - } - - return icon; - } - - public void put(PwIconCustom icon) { - customCache.put(icon.getUUID(), icon); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.kt new file mode 100644 index 000000000..51b619645 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconFactory.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2019 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import org.apache.commons.collections.map.AbstractReferenceMap +import org.apache.commons.collections.map.ReferenceMap + +import java.util.UUID + +class PwIconFactory { + /** customIconMap + * Cache for icon drawable. + * Keys: Integer, Values: PwIconStandard + */ + private val cache = ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK) + + /** standardIconMap + * Cache for icon drawable. + * Keys: UUID, Values: PwIconCustom + */ + private val customCache = ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK) + + val unknownIcon: PwIconStandard + get() = getIcon(PwIcon.UNKNOWN) + + val keyIcon: PwIconStandard + get() = getIcon(PwIconStandard.KEY) + + val trashIcon: PwIconStandard + get() = getIcon(PwIconStandard.TRASH) + + val folderIcon: PwIconStandard + get() = getIcon(PwIconStandard.FOLDER) + + fun getIcon(iconId: Int): PwIconStandard { + var icon: PwIconStandard? = cache[iconId] as PwIconStandard? + + if (icon == null) { + icon = PwIconStandard(iconId) + cache[iconId] = icon + } + + return icon + } + + fun getIcon(iconUuid: UUID): PwIconCustom { + var icon: PwIconCustom? = customCache[iconUuid] as PwIconCustom? + + if (icon == null) { + icon = PwIconCustom(iconUuid) + customCache[iconUuid] = icon + } + + return icon + } + + fun put(icon: PwIconCustom) { + customCache[icon.uuid] = icon + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java deleted file mode 100644 index 7f0aad21d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -public class PwIconStandard extends PwIcon { - private final int iconId; - - public static final int KEY = 0; - public static final int TRASH = 43; - public static final int FOLDER = 48; - - public PwIconStandard() { - this.iconId = KEY; - } - - public PwIconStandard(int iconId) { - this.iconId = iconId; - } - - public PwIconStandard(PwIconStandard icon) { - this.iconId = icon.iconId; - } - - protected PwIconStandard(Parcel in) { - super(in); - iconId = in.readInt(); - } - - @Override - public boolean isUnknown() { - return iconId == UNKNOWN; - } - - public int getIconId() { - return iconId; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(iconId); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwIconStandard createFromParcel(Parcel in) { - return new PwIconStandard(in); - } - - @Override - public PwIconStandard[] newArray(int size) { - return new PwIconStandard[size]; - } - }; - - @Override - public boolean isMetaStreamIcon() { - return iconId == 0; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + iconId; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - PwIconStandard other = (PwIconStandard) obj; - if (iconId != other.iconId) - return false; - return true; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt new file mode 100644 index 000000000..421c73dd0 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2019 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +class PwIconStandard : PwIcon { + override val iconId: Int + + override val isUnknown: Boolean + get() = iconId == PwIcon.UNKNOWN + + override val isMetaStreamIcon: Boolean + get() = iconId == 0 + + constructor() { + this.iconId = KEY + } + + constructor(iconId: Int) { + this.iconId = iconId + } + + constructor(icon: PwIconStandard) { + this.iconId = icon.iconId + } + + constructor(parcel: Parcel) { + iconId = parcel.readInt() + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(iconId) + } + + override fun hashCode(): Int { + val prime = 31 + var result = 1 + result = prime * result + iconId + return result + } + + override fun equals(other: Any?): Boolean { + if (this === other) + return true + if (other == null) + return false + if (other !is PwIconStandard) { + return false + } + return iconId == other.iconId + } + + companion object { + + const val KEY = 0 + const val TRASH = 43 + const val FOLDER = 48 + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PwIconStandard { + return PwIconStandard(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt index 3b1b1d2ce..6ff613d7f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdInt.kt @@ -46,6 +46,10 @@ class PwNodeIdInt : PwNodeId { } override fun equals(other: Any?): Boolean { + if (this === other) + return true + if (other == null) + return false if (other !is PwNodeIdInt) { return false } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt index f9e711969..aac6d7cf8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeIdUUID.kt @@ -46,6 +46,10 @@ class PwNodeIdUUID : PwNodeId { } override fun equals(other: Any?): Boolean { + if (this === other) + return true + if (other == null) + return false if (other !is PwNodeIdUUID) { return false } @@ -63,8 +67,8 @@ class PwNodeIdUUID : PwNodeId { companion object { @JvmField val CREATOR: Parcelable.Creator = object : Parcelable.Creator { - override fun createFromParcel(`in`: Parcel): PwNodeIdUUID { - return PwNodeIdUUID(`in`) + override fun createFromParcel(parcel: Parcel): PwNodeIdUUID { + return PwNodeIdUUID(parcel) } override fun newArray(size: Int): Array { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.kt similarity index 67% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.kt index 756c8f2c1..5845bf5ed 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,20 +17,16 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.element; +package com.kunzisoft.keepass.database.element -public enum PwVersion { +enum class PwVersion { UNKNOWN, V3, V4; - @Override - public String toString() { - switch (this) { - case V3: - return "KeePass 1"; - case V4: - return "KeePass 2"; - default: - return "unknown"; + override fun toString(): String { + return when (this) { + V3 -> "KeePass 1" + V4 -> "KeePass 2" + else -> "unknown" } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index ea4ec5cc5..04f272245 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -316,8 +316,8 @@ public class PwDbV4Output extends PwDbOutput { writeObject(PwDatabaseV4XML.ElemNotes, group.getNotes()); writeObject(PwDatabaseV4XML.ElemIcon, group.getIconStandard().getIconId()); - if (!group.getIconCustom().equals(PwIconCustom.ZERO)) { - writeObject(PwDatabaseV4XML.ElemCustomIconID, group.getIconCustom().getUUID()); + if (!group.getIconCustom().equals(PwIconCustom.Companion.getZERO())) { + writeObject(PwDatabaseV4XML.ElemCustomIconID, group.getIconCustom().getUuid()); } writeList(PwDatabaseV4XML.ElemTimes, group); @@ -341,8 +341,8 @@ public class PwDbV4Output extends PwDbOutput { writeObject(PwDatabaseV4XML.ElemUuid, entry.getNodeId().getId()); writeObject(PwDatabaseV4XML.ElemIcon, entry.getIconStandard().getIconId()); - if (!entry.getIconCustom().equals(PwIconCustom.ZERO)) { - writeObject(PwDatabaseV4XML.ElemCustomIconID, entry.getIconCustom().getUUID()); + if (!entry.getIconCustom().equals(PwIconCustom.Companion.getZERO())) { + writeObject(PwDatabaseV4XML.ElemCustomIconID, entry.getIconCustom().getUuid()); } writeObject(PwDatabaseV4XML.ElemFgColor, entry.getForegroundColor()); @@ -713,7 +713,7 @@ public class PwDbV4Output extends PwDbOutput { for (PwIconCustom icon : customIcons) { xml.startTag(null, PwDatabaseV4XML.ElemCustomIconItem); - writeObject(PwDatabaseV4XML.ElemCustomIconItemID, icon.getUUID()); + writeObject(PwDatabaseV4XML.ElemCustomIconItemID, icon.getUuid()); writeObject(PwDatabaseV4XML.ElemCustomIconItemData, String.valueOf(Base64Coder.encode(icon.getImageData()))); xml.endTag(null, PwDatabaseV4XML.ElemCustomIconItem); diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java index cc85acfab..cffa1bd1f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java @@ -287,13 +287,10 @@ public class IconDrawableFactory { return blank; } - Drawable draw = (Drawable) customIconMap.get(icon.getUUID()); + Drawable draw = (Drawable) customIconMap.get(icon.getUuid()); if (draw == null) { - if (icon.getImageData() == null) { - return blank; - } - + Bitmap bitmap = BitmapFactory.decodeByteArray(icon.getImageData(), 0, icon.getImageData().length); // Could not understand custom icon @@ -304,7 +301,7 @@ public class IconDrawableFactory { bitmap = resize(bitmap); draw = new BitmapDrawable(context.getResources(), bitmap); - customIconMap.put(icon.getUUID(), draw); + customIconMap.put(icon.getUuid(), draw); } return draw; From fadf78aabd0d15c9bb1cef98cc2311a51cb2c90e Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 13:51:03 +0200 Subject: [PATCH 112/289] Remove PwVersion --- .../keepass/database/element/Database.kt | 2 +- .../keepass/database/element/PwDatabase.java | 2 +- .../database/element/PwDatabaseV3.java | 4 +-- .../database/element/PwDatabaseV4.java | 4 +-- .../keepass/database/element/PwVersion.kt | 32 ------------------- 5 files changed, 6 insertions(+), 38 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 26628f9ce..8f8b48443 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -432,7 +432,7 @@ class Database { } fun getVersion(): String { - return pwDatabaseV3?.version?.toString() ?: pwDatabaseV4?.version?.toString() ?: PwVersion.UNKNOWN.toString() + return pwDatabaseV3?.version ?: pwDatabaseV4?.version ?: "unknown" } fun containsName(): Boolean { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index b55915b31..1182a6b9d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -52,7 +52,7 @@ public abstract class PwDatabase, Entry e protected LinkedHashMap groupIndexes = new LinkedHashMap<>(); protected LinkedHashMap entryIndexes = new LinkedHashMap<>(); - public abstract PwVersion getVersion(); + public abstract String getVersion(); public PwIconFactory getIconFactory() { return iconFactory; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index 288953bff..d0ba07261 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -51,8 +51,8 @@ public class PwDatabaseV3 extends PwDatabase { } @Override - public PwVersion getVersion() { - return PwVersion.V3; + public String getVersion() { + return "KeePass 1"; } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 4d0d0e54d..0c47d85d1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -96,8 +96,8 @@ public class PwDatabaseV4 extends PwDatabase { public PwDatabaseV4() {} @Override - public PwVersion getVersion() { - return PwVersion.V4; + public String getVersion() { + return "KeePass 2"; } public String getName() { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.kt deleted file mode 100644 index 5845bf5ed..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwVersion.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element - -enum class PwVersion { - UNKNOWN, V3, V4; - - override fun toString(): String { - return when (this) { - V3 -> "KeePass 1" - V4 -> "KeePass 2" - else -> "unknown" - } - } -} From 0b53e8476196a83517794046393b3bf8ccfa5ad1 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 13:58:41 +0200 Subject: [PATCH 113/289] Kotlinized small code --- .../keepass/crypto/engine/AesEngine.java | 2 +- .../keepass/database/element/PwDatabase.java | 2 +- .../database/element/PwDatabaseV3.java | 4 +- .../database/element/PwDatabaseV4.java | 2 +- .../database/element/PwDbHeaderFactory.java | 33 -------- .../database/element/PwDeletedObject.java | 79 ------------------- .../database/element/PwDeletedObject.kt | 54 +++++++++++++ .../element/PwEncryptionAlgorithm.java | 74 ----------------- .../database/element/PwEncryptionAlgorithm.kt | 64 +++++++++++++++ .../keepass/database/load/ImporterV3.java | 4 +- .../keepass/database/save/PwDbV3Output.java | 4 +- 11 files changed, 127 insertions(+), 195 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderFactory.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java index 4ddc66664..2d3bcdd2b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/engine/AesEngine.java @@ -52,6 +52,6 @@ public class AesEngine extends CipherEngine { @Override public PwEncryptionAlgorithm getPwEncryptionAlgorithm() { - return PwEncryptionAlgorithm.AES_Rijndael; + return PwEncryptionAlgorithm.AESRijndael; } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java index 1182a6b9d..4e899d81d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.java @@ -214,7 +214,7 @@ public abstract class PwDatabase, Entry e public PwEncryptionAlgorithm getEncryptionAlgorithm() { if (algorithm != null) return algorithm; - return PwEncryptionAlgorithm.AES_Rijndael; + return PwEncryptionAlgorithm.AESRijndael; } public void setEncryptionAlgorithm(PwEncryptionAlgorithm algorithm) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index d0ba07261..8399b0fb4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -46,7 +46,7 @@ public class PwDatabaseV3 extends PwDatabase { protected PwGroupV3 rootGroup; public PwDatabaseV3() { - algorithm = PwEncryptionAlgorithm.AES_Rijndael; + algorithm = PwEncryptionAlgorithm.AESRijndael; numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS; } @@ -58,7 +58,7 @@ public class PwDatabaseV3 extends PwDatabase { @Override public List getAvailableEncryptionAlgorithms() { List list = new ArrayList<>(); - list.add(PwEncryptionAlgorithm.AES_Rijndael); + list.add(PwEncryptionAlgorithm.AESRijndael); return list; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 0c47d85d1..8ad0c272b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -127,7 +127,7 @@ public class PwDatabaseV4 extends PwDatabase { @Override public List getAvailableEncryptionAlgorithms() { List list = new ArrayList<>(); - list.add(PwEncryptionAlgorithm.AES_Rijndael); + list.add(PwEncryptionAlgorithm.AESRijndael); list.add(PwEncryptionAlgorithm.Twofish); list.add(PwEncryptionAlgorithm.ChaCha20); return list; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderFactory.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderFactory.java deleted file mode 100644 index 4d8a07e13..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderFactory.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -public class PwDbHeaderFactory { - public static PwDbHeader getInstance(PwDatabase db) { - if (db instanceof PwDatabaseV3) { - return new PwDbHeaderV3(); - } else if (db instanceof PwDatabaseV4) { - return new PwDbHeaderV4((PwDatabaseV4)db); - } else { - throw new RuntimeException("Not implemented."); - - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java deleted file mode 100644 index e842838e5..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import java.util.Date; -import java.util.UUID; - -public class PwDeletedObject { - - public UUID uuid; - private Date deletionTime; - - public PwDeletedObject() { - - } - - public PwDeletedObject(UUID u) { - this(u, new Date()); - } - - public PwDeletedObject(UUID u, Date d) { - uuid = u; - deletionTime = d; - } - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public Date getDeletionTime() { - if ( deletionTime == null ) { - return new Date(System.currentTimeMillis()); - } - - return deletionTime; - } - - public void setDeletionTime(Date date) { - deletionTime = date; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - else if (o == null) { - return false; - } - else if (!(o instanceof PwDeletedObject)) { - return false; - } - - PwDeletedObject rhs = (PwDeletedObject) o; - - return uuid.equals(rhs.uuid); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.kt new file mode 100644 index 000000000..f0d78f5dd --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDeletedObject.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import java.util.Date +import java.util.UUID + +class PwDeletedObject { + + var uuid: UUID = PwDatabase.UUID_ZERO + var deletionTime: Date? = null + get() = if (field == null) { + Date(System.currentTimeMillis()) + } else field + + constructor() + + @JvmOverloads + constructor(uuid: UUID, deletionTime: Date = Date()) { + this.uuid = uuid + this.deletionTime = deletionTime + } + + override fun equals(other: Any?): Boolean { + if (this === other) + return true + if (other == null) + return false + if (other !is PwDeletedObject) + return false + return uuid == other.uuid + } + + override fun hashCode(): Int { + return uuid.hashCode() + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.java deleted file mode 100644 index 8974ce6fc..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.content.res.Resources; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.crypto.engine.AesEngine; -import com.kunzisoft.keepass.crypto.engine.ChaCha20Engine; -import com.kunzisoft.keepass.crypto.engine.CipherEngine; -import com.kunzisoft.keepass.crypto.engine.TwofishEngine; -import com.kunzisoft.keepass.database.ObjectNameResource; - -import java.util.UUID; - -public enum PwEncryptionAlgorithm implements ObjectNameResource { - - AES_Rijndael, - Twofish, - ChaCha20; - - public String getName(Resources resources) { - switch (this) { - default: - case AES_Rijndael: - return resources.getString(R.string.encryption_rijndael); - case Twofish: - return resources.getString(R.string.encryption_twofish); - case ChaCha20: - return resources.getString(R.string.encryption_chacha20); - } - } - - public CipherEngine getCipherEngine() { - switch (this) { - default: - case AES_Rijndael: - return new AesEngine(); - case Twofish: - return new TwofishEngine(); - case ChaCha20: - return new ChaCha20Engine(); - } - } - - public UUID getDataCipher() { - switch (this) { - default: - case AES_Rijndael: - return AesEngine.CIPHER_UUID; - case Twofish: - return TwofishEngine.CIPHER_UUID; - case ChaCha20: - return ChaCha20Engine.CIPHER_UUID; - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.kt new file mode 100644 index 000000000..72ce3cffb --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEncryptionAlgorithm.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.content.res.Resources + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.crypto.engine.AesEngine +import com.kunzisoft.keepass.crypto.engine.ChaCha20Engine +import com.kunzisoft.keepass.crypto.engine.CipherEngine +import com.kunzisoft.keepass.crypto.engine.TwofishEngine +import com.kunzisoft.keepass.database.ObjectNameResource + +import java.util.UUID + +enum class PwEncryptionAlgorithm : ObjectNameResource { + + AESRijndael, + Twofish, + ChaCha20; + + val cipherEngine: CipherEngine + get() { + return when (this) { + AESRijndael -> AesEngine() + Twofish -> TwofishEngine() + ChaCha20 -> ChaCha20Engine() + } + } + + val dataCipher: UUID + get() { + return when (this) { + AESRijndael -> AesEngine.CIPHER_UUID + Twofish -> TwofishEngine.CIPHER_UUID + ChaCha20 -> ChaCha20Engine.CIPHER_UUID + } + } + + override fun getName(resources: Resources): String { + return when (this) { + AESRijndael -> resources.getString(R.string.encryption_rijndael) + Twofish -> resources.getString(R.string.encryption_twofish) + ChaCha20 -> resources.getString(R.string.encryption_chacha20) + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index 83aaa6301..966166e9b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -110,7 +110,7 @@ public class ImporterV3 extends Importer { // Select algorithm if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) { - databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.AES_Rijndael); + databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.AESRijndael); } else if( (hdr.flags & PwDbHeaderV3.FLAG_TWOFISH) != 0 ) { databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.Twofish); } else { @@ -130,7 +130,7 @@ public class ImporterV3 extends Importer { // Initialize Rijndael algorithm Cipher cipher; try { - if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { + if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AESRijndael) { cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); } else if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) { cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING"); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java index 208494065..017f7340f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java @@ -69,7 +69,7 @@ public class PwDbV3Output extends PwDbOutput { Cipher cipher; try { - if (mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { + if (mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AESRijndael) { cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); } else if (mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish){ cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING"); @@ -112,7 +112,7 @@ public class PwDbV3Output extends PwDbOutput { header.signature2 = PwDbHeaderV3.DBSIG_2; header.flags = PwDbHeaderV3.FLAG_SHA2; - if ( mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AES_Rijndael) { + if ( mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AESRijndael) { header.flags |= PwDbHeaderV3.FLAG_RIJNDAEL; } else if ( mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) { header.flags |= PwDbHeaderV3.FLAG_TWOFISH; From 575e96762781beb665aa0f843b6440cb71bbdd71 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 1 Jun 2019 15:01:02 +0200 Subject: [PATCH 114/289] Kotlinized PwNode --- .../keepass/database/element/Database.kt | 2 +- .../database/element/EntryVersioned.kt | 76 +++--- .../database/element/GroupVersioned.kt | 78 +++---- .../NodeTimeInterface.kt} | 23 +- .../NodeV4Interface.kt} | 12 +- .../keepass/database/element/PwEntryV3.java | 9 +- .../keepass/database/element/PwEntryV4.java | 9 +- .../keepass/database/element/PwGroupV3.java | 7 +- .../keepass/database/element/PwGroupV4.java | 11 +- .../keepass/database/element/PwNode.java | 216 ------------------ .../keepass/database/element/PwNode.kt | 162 +++++++++++++ .../database/element/PwNodeInterface.kt | 7 +- .../keepass/database/load/ImporterV4.java | 4 +- .../keepass/database/save/PwDbV4Output.java | 2 +- 14 files changed, 270 insertions(+), 348 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/database/{SmallTimeInterface.java => element/NodeTimeInterface.kt} (57%) rename app/src/main/java/com/kunzisoft/keepass/database/{ITimeLogger.java => element/NodeV4Interface.kt} (73%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 8f8b48443..99dfeaf46 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -569,7 +569,7 @@ class Database { */ fun copyEntryTo(entryToCopy: EntryVersioned, newParent: GroupVersioned): EntryVersioned? { val entryCopied = EntryVersioned(entryToCopy) - entryCopied.nodeId = pwDatabaseV3?.newEntryId() ?: pwDatabaseV4?.newEntryId() + entryCopied.nodeId = pwDatabaseV3?.newEntryId() ?: pwDatabaseV4?.newEntryId() ?: PwNodeIdUUID() entryCopied.parent = newParent entryCopied.title += " (~)" addEntryTo(entryCopied, newParent) 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 8afdc08e8..56fa6c22c 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 @@ -56,8 +56,8 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { dest.writeParcelable(pwEntryV4, flags) } - var nodeId: PwNodeId? - get() = pwEntryV4?.nodeId ?: pwEntryV3?.nodeId + var nodeId: PwNodeId + get() = pwEntryV4?.nodeId ?: pwEntryV3?.nodeId ?: PwNodeIdUUID() set(value) { pwEntryV3?.nodeId = value pwEntryV4?.nodeId = value @@ -111,50 +111,40 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { override val isSearchingEnabled: Boolean get() = pwEntryV3?.isSearchingEnabled ?: pwEntryV4?.isSearchingEnabled ?: false - override fun getLastModificationTime(): PwDate? { - return pwEntryV3?.lastModificationTime ?: pwEntryV4?.lastModificationTime - } + override var creationTime: PwDate + get() = pwEntryV3?.creationTime ?: pwEntryV4?.creationTime ?: PwDate() + set(value) { + pwEntryV3?.creationTime = value + pwEntryV4?.creationTime = value + } - override fun setLastModificationTime(date: PwDate) { - pwEntryV3?.lastModificationTime = date - pwEntryV4?.lastModificationTime = date - } + override var lastModificationTime: PwDate + get() = pwEntryV3?.lastModificationTime ?: pwEntryV4?.lastModificationTime ?: PwDate() + set(value) { + pwEntryV3?.lastModificationTime = value + pwEntryV4?.lastModificationTime = value + } - override fun getCreationTime(): PwDate? { - return pwEntryV3?.creationTime ?: pwEntryV4?.creationTime - } + override var lastAccessTime: PwDate + get() = pwEntryV3?.lastAccessTime ?: pwEntryV4?.lastAccessTime ?: PwDate() + set(value) { + pwEntryV3?.lastAccessTime = value + pwEntryV4?.lastAccessTime = value + } - override fun setCreationTime(date: PwDate) { - pwEntryV3?.creationTime = date - pwEntryV4?.creationTime = date - } + override var expiryTime: PwDate + get() = pwEntryV3?.expiryTime ?: pwEntryV4?.expiryTime ?: PwDate() + set(value) { + pwEntryV3?.expiryTime = value + pwEntryV4?.expiryTime = value + } - override fun getLastAccessTime(): PwDate? { - return pwEntryV3?.lastAccessTime ?: pwEntryV4?.lastAccessTime - } - - override fun setLastAccessTime(date: PwDate) { - pwEntryV3?.lastAccessTime = date - pwEntryV4?.lastAccessTime = date - } - - override fun getExpiryTime(): PwDate? { - return pwEntryV3?.expiryTime ?: pwEntryV4?.expiryTime - } - - override fun setExpiryTime(date: PwDate) { - pwEntryV3?.expiryTime = date - pwEntryV4?.expiryTime = date - } - - override fun isExpires(): Boolean { - return pwEntryV3?.isExpires ?: pwEntryV4?.isExpires ?: false - } - - override fun setExpires(exp: Boolean) { - pwEntryV3?.isExpires = exp - pwEntryV4?.isExpires = exp - } + override var isExpires: Boolean + get() =pwEntryV3?.isExpires ?: pwEntryV4?.isExpires ?: false + set(value) { + pwEntryV3?.isExpires = value + pwEntryV4?.isExpires = value + } override var username: String get() = pwEntryV3?.username ?: pwEntryV4?.username ?: "" @@ -189,7 +179,7 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { pwEntryV4?.touchLocation() } - fun isTan(): Boolean { + private fun isTan(): Boolean { return title == PMS_TAN_ENTRY && username.isNotEmpty() } 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 3b47487c1..55ebbe8ca 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 @@ -117,50 +117,40 @@ class GroupVersioned : NodeVersioned, PwGroupInterface { val children = ArrayList() @@ -248,8 +238,8 @@ class GroupVersioned : NodeVersioned, PwGroupInterface? - get() = pwGroupV3?.nodeId + var nodeIdV3: PwNodeId + get() = pwGroupV3?.nodeId ?: PwNodeIdInt() set(value) { pwGroupV3?.nodeId = value } fun setNodeId(id: PwNodeIdInt) { @@ -270,8 +260,8 @@ class GroupVersioned : NodeVersioned, PwGroupInterface? - get() = pwGroupV4?.nodeId + var nodeIdV4: PwNodeId + get() = pwGroupV4?.nodeId ?: PwNodeIdUUID() set(value) { pwGroupV4?.nodeId = value } fun setNodeId(id: PwNodeIdUUID) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SmallTimeInterface.java b/app/src/main/java/com/kunzisoft/keepass/database/element/NodeTimeInterface.kt similarity index 57% rename from app/src/main/java/com/kunzisoft/keepass/database/SmallTimeInterface.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/NodeTimeInterface.kt index fe192bbe4..44223b6fa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SmallTimeInterface.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/NodeTimeInterface.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,24 +17,17 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element -import com.kunzisoft.keepass.database.element.PwDate; +interface NodeTimeInterface { -public interface SmallTimeInterface { + var creationTime: PwDate - PwDate getLastModificationTime(); - void setLastModificationTime(PwDate date); + var lastModificationTime: PwDate - PwDate getCreationTime(); - void setCreationTime(PwDate date); + var lastAccessTime: PwDate - PwDate getLastAccessTime(); - void setLastAccessTime(PwDate date); + var expiryTime: PwDate - PwDate getExpiryTime(); - void setExpiryTime(PwDate date); - - boolean isExpires(); - void setExpires(boolean exp); + var isExpires: Boolean } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java b/app/src/main/java/com/kunzisoft/keepass/database/element/NodeV4Interface.kt similarity index 73% rename from app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/NodeV4Interface.kt index df4ce2290..f49ac2d97 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ITimeLogger.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/NodeV4Interface.kt @@ -17,16 +17,12 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database.element -import com.kunzisoft.keepass.database.element.PwDate; +interface NodeV4Interface : NodeTimeInterface { -public interface ITimeLogger extends SmallTimeInterface { + var usageCount: Long - long getUsageCount(); - void setUsageCount(long count); - - PwDate getLocationChanged(); - void setLocationChanged(PwDate date); + var locationChanged: PwDate } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java index bbae0eef1..a701ea8f3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java @@ -43,6 +43,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import android.support.annotation.NonNull; import java.io.UnsupportedEncodingException; import java.util.Arrays; @@ -87,13 +88,15 @@ public class PwEntryV3 extends PwEntry { private String binaryDesc = ""; private byte[] binaryData = new byte[0]; + @NonNull @Override - PwNodeId initNodeId() { + protected PwNodeId initNodeId() { return new PwNodeIdUUID(); } - @Override - PwNodeId copyNodeId(PwNodeId nodeId) { + @NonNull + @Override + protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { return new PwNodeIdUUID(nodeId.getId()); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java index ca025125f..c99d401b7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java @@ -24,7 +24,6 @@ import android.os.Parcel; import android.support.annotation.NonNull; import com.kunzisoft.keepass.database.AutoType; import com.kunzisoft.keepass.database.ExtraFields; -import com.kunzisoft.keepass.database.ITimeLogger; import com.kunzisoft.keepass.database.security.ProtectedBinary; import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.utils.MemUtil; @@ -37,7 +36,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.UUID; -public class PwEntryV4 extends PwEntry implements ITimeLogger { +public class PwEntryV4 extends PwEntry implements NodeV4Interface { public static final String STR_TITLE = "Title"; public static final String STR_USERNAME = "UserName"; @@ -64,13 +63,15 @@ public class PwEntryV4 extends PwEntry implements ITimeLog private String additional = ""; private String tags = ""; + @NonNull @Override - PwNodeId initNodeId() { + protected PwNodeId initNodeId() { return new PwNodeIdUUID(); } + @NonNull @Override - PwNodeId copyNodeId(PwNodeId nodeId) { + protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { return new PwNodeIdUUID(nodeId.getId()); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java index d07254e3f..551ea4657 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; +import android.support.annotation.NonNull; public class PwGroupV3 extends PwGroup { @@ -28,13 +29,15 @@ public class PwGroupV3 extends PwGroup { /** Used by KeePass internally, don't use */ private int flags; + @NonNull @Override - PwNodeId initNodeId() { + protected PwNodeId initNodeId() { return new PwNodeIdInt(); } + @NonNull @Override - PwNodeId copyNodeId(PwNodeId nodeId) { + protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { return new PwNodeIdInt(nodeId.getId()); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java index 58b5829c1..220a3a7d0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java @@ -21,13 +21,12 @@ package com.kunzisoft.keepass.database.element; import android.os.Parcel; import android.support.annotation.NonNull; -import com.kunzisoft.keepass.database.ITimeLogger; import java.util.HashMap; import java.util.Map; import java.util.UUID; -public class PwGroupV4 extends PwGroup implements ITimeLogger { +public class PwGroupV4 extends PwGroup implements NodeV4Interface { private PwIconCustom customIcon = PwIconCustom.Companion.getZERO(); private long usageCount = 0; @@ -41,13 +40,15 @@ public class PwGroupV4 extends PwGroup implements IT private Boolean enableSearching = null; private UUID lastTopVisibleEntry = PwDatabase.UUID_ZERO; - @Override - PwNodeId initNodeId() { + @NonNull + @Override + protected PwNodeId initNodeId() { return new PwNodeIdUUID(); } + @NonNull @Override - PwNodeId copyNodeId(PwNodeId nodeId) { + protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { return new PwNodeIdUUID(nodeId.getId()); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java deleted file mode 100644 index 5af7ad646..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.NonNull; -import org.joda.time.LocalDate; - -/** - * Abstract class who manage Groups and Entries - */ -public abstract class PwNode - < - IdType, - Parent extends PwGroupInterface, - Entry extends PwEntryInterface - > - implements PwNodeInterface, Parcelable { - - private PwNodeId nodeId = initNodeId(); - private Parent parent = null; - private PwIcon icon = new PwIconStandard(); - protected PwDate creation = new PwDate(); - private PwDate lastMod = new PwDate(); - private PwDate lastAccess = new PwDate(); - private PwDate expireDate = PwDate.PW_NEVER_EXPIRE; - - abstract PwNodeId initNodeId(); - abstract PwNodeId copyNodeId(PwNodeId nodeId); - - protected PwNode() {} - - protected PwNode(Parcel parcel) { - nodeId = parcel.readParcelable(PwNodeId.class.getClassLoader()); - parent = readParentParcelable(parcel); - icon = parcel.readParcelable(PwIconStandard.class.getClassLoader()); - creation = parcel.readParcelable(PwDate.class.getClassLoader()); - lastMod = parcel.readParcelable(PwDate.class.getClassLoader()); - lastAccess = parcel.readParcelable(PwDate.class.getClassLoader()); - expireDate = parcel.readParcelable(PwDate.class.getClassLoader()); - } - - protected abstract Parent readParentParcelable(Parcel parcel); - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(nodeId, flags); - dest.writeParcelable(parent, flags); - dest.writeParcelable(icon, flags); - dest.writeParcelable(creation, flags); - dest.writeParcelable(lastMod, flags); - dest.writeParcelable(lastAccess, flags); - dest.writeParcelable(expireDate, flags); - } - - @Override - public int describeContents() { - return 0; - } - - protected void updateWith(PwNode source) { - this.nodeId = copyNodeId(source.nodeId); - this.parent = source.parent; - this.icon = source.icon; - this.creation = new PwDate(source.creation); - this.lastMod = new PwDate(source.lastMod); - this.lastAccess = new PwDate(source.lastAccess); - this.expireDate = new PwDate(source.expireDate); - } - - public IdType getId() { - return getNodeId().getId(); - } - - public PwNodeId getNodeId() { - return nodeId; - } - - public void setNodeId(PwNodeId id) { - this.nodeId = id; - } - - /** - * @return Visual icon - */ - @NonNull - public PwIcon getIcon() { - return icon; - } - - @Override - public void setIcon(@NonNull PwIcon icon) { - this.icon = icon; - } - - @Override - public Parent getParent() { - return parent; - } - - @Override - public void setParent(Parent parent) { - this.parent = parent; - } - - /** - * @return true if parent is present (false if not present, can be a root or a detach element) - */ - public boolean containsParent() { - return getParent() != null; - } - - public PwDate getCreationTime() { - return creation; - } - - public void setCreationTime(PwDate date) { - creation = date; - } - - public PwDate getLastModificationTime() { - return lastMod; - } - - public void setLastModificationTime(PwDate date) { - lastMod = date; - } - - public PwDate getLastAccessTime() { - return lastAccess; - } - - public void setLastAccessTime(PwDate date) { - lastAccess = date; - } - - public PwDate getExpiryTime() { - return expireDate; - } - - public void setExpiryTime(PwDate date) { - expireDate = date; - } - - public void setExpires(boolean expires) { - if (!expires) { - expireDate = PwDate.PW_NEVER_EXPIRE; - } - } - - public boolean isExpires() { - // If expireDate is before NEVER_EXPIRE date less 1 month (to be sure) - return expireDate.getDate().before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()); - } - - @Override - public boolean isContainedIn(Parent container) { - Parent cur = this.getParent(); - while (cur != null) { - if (cur.equals(container)) { - return true; - } - cur = cur.getParent(); - } - return false; - } - - @Override - public void touch(boolean modified, boolean touchParents) { - PwDate now = new PwDate(); - setLastAccessTime(now); - - if (modified) { - setLastModificationTime(now); - } - - Parent parent = getParent(); - if (touchParents && parent != null) { - parent.touch(modified, true); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - PwNode pwNode = (PwNode) o; - return getType().equals(pwNode.getType()) - && (getNodeId() != null ? getNodeId().equals(pwNode.getNodeId()) : pwNode.getNodeId() == null); - } - - @Override - public int hashCode() { - return getNodeId() != null ? getNodeId().hashCode() : 0; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt new file mode 100644 index 000000000..1fc8436e1 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt @@ -0,0 +1,162 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable +import org.joda.time.LocalDate + +/** + * Abstract class who manage Groups and Entries + */ +abstract class PwNode, Entry : PwEntryInterface> : PwNodeInterface, Parcelable { + + var nodeId: PwNodeId = this.initNodeId() + + private var mParent: Parent? = null + private var mIcon: PwIcon = PwIconStandard() + private var mCreationDate = PwDate() + private var mLastModificationDate = PwDate() + private var mLastAccessDate = PwDate() + private var mExpireDate = PwDate.PW_NEVER_EXPIRE + + protected abstract fun initNodeId(): PwNodeId + protected abstract fun copyNodeId(nodeId: PwNodeId): PwNodeId + protected abstract fun readParentParcelable(parcel: Parcel): Parent + + protected constructor() + + protected constructor(parcel: Parcel) { + this.nodeId = parcel.readParcelable(PwNodeId::class.java.classLoader) + this.mParent = this.readParentParcelable(parcel) + this.mIcon = parcel.readParcelable(PwIconStandard::class.java.classLoader) + this.mCreationDate = parcel.readParcelable(PwDate::class.java.classLoader) + this.mLastModificationDate = parcel.readParcelable(PwDate::class.java.classLoader) + this.mLastAccessDate = parcel.readParcelable(PwDate::class.java.classLoader) + this.mExpireDate = parcel.readParcelable(PwDate::class.java.classLoader) + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeParcelable(nodeId, flags) + dest.writeParcelable(mParent, flags) + dest.writeParcelable(mIcon, flags) + dest.writeParcelable(mCreationDate, flags) + dest.writeParcelable(mLastModificationDate, flags) + dest.writeParcelable(mLastAccessDate, flags) + dest.writeParcelable(mExpireDate, flags) + } + + override fun describeContents(): Int { + return 0 + } + + protected fun updateWith(source: PwNode) { + this.nodeId = copyNodeId(source.nodeId) + this.mParent = source.parent + this.mIcon = source.icon + this.mCreationDate = PwDate(source.mCreationDate) + this.mLastModificationDate = PwDate(source.mLastModificationDate) + this.mLastAccessDate = PwDate(source.mLastAccessDate) + this.mExpireDate = PwDate(source.mExpireDate) + } + + val id: IdType + get() = nodeId.id + + override var parent: Parent? + get() = mParent + set(value) { mParent = value } + + override var icon: PwIcon + get() = mIcon + set(value) { mIcon = value } + + override var creationTime: PwDate + get() = mCreationDate + set(value) { mCreationDate = value } + + override var lastModificationTime: PwDate + get() = mLastModificationDate + set(value) { mLastModificationDate = value } + + override var lastAccessTime: PwDate + get() = mLastAccessDate + set(value) { mLastAccessDate = value } + + override var expiryTime: PwDate + get() = mExpireDate + set(value) { mExpireDate = value } + + override var isExpires: Boolean + // If expireDate is before NEVER_EXPIRE date less 1 month (to be sure) + get() = mExpireDate.date.before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()) + set(value) { + if (!value) { + mExpireDate = PwDate.PW_NEVER_EXPIRE + } + } + + /** + * @return true if parent is present (false if not present, can be a root or a detach element) + */ + override fun containsParent(): Boolean { + return parent != null + } + + override fun isContainedIn(container: Parent): Boolean { + var cur = this.parent + while (cur != null) { + if (cur == container) { + return true + } + cur = cur.parent + } + return false + } + + override fun touch(modified: Boolean, touchParents: Boolean) { + val now = PwDate() + lastAccessTime = now + + if (modified) { + lastModificationTime = now + } + + if (touchParents) { + parent?.touch(modified, true) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) + return true + if (other == null) + return false + if (other !is PwNode<*, *, *>) { + return false + } + return type == other.type && nodeId == other.nodeId + } + + override fun hashCode(): Int { + return nodeId.hashCode() + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt index c63d1c028..592519291 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt @@ -1,9 +1,8 @@ package com.kunzisoft.keepass.database.element import android.os.Parcelable -import com.kunzisoft.keepass.database.SmallTimeInterface -interface PwNodeInterface : SmallTimeInterface, Parcelable { +interface PwNodeInterface : NodeTimeInterface, Parcelable { var title: String @@ -26,7 +25,7 @@ interface PwNodeInterface : SmallTimeInterface, Parcelable { fun containsParent(): Boolean - fun touch(modified: Boolean, touchParents: Boolean) - fun isContainedIn(container: ParentGroup): Boolean + + fun touch(modified: Boolean, touchParents: Boolean) } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 211fbf57e..047669976 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -24,7 +24,7 @@ import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.crypto.CipherFactory; import com.kunzisoft.keepass.crypto.PwStreamCipherFactory; import com.kunzisoft.keepass.crypto.engine.CipherEngine; -import com.kunzisoft.keepass.database.ITimeLogger; +import com.kunzisoft.keepass.database.element.NodeV4Interface; import com.kunzisoft.keepass.database.PwCompressionAlgorithm; import com.kunzisoft.keepass.database.element.*; import com.kunzisoft.keepass.database.exception.ArcFourException; @@ -641,7 +641,7 @@ public class ImporterV4 extends Importer { case GroupTimes: case EntryTimes: - ITimeLogger tl; + NodeV4Interface tl; if ( ctx == KdbContext.GroupTimes ) { tl = ctxGroup; } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index 04f272245..123c9ce89 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -675,7 +675,7 @@ public class PwDbV4Output extends PwDbOutput { } - private void writeList(String name, ITimeLogger it) throws IllegalArgumentException, IllegalStateException, IOException { + private void writeList(String name, NodeV4Interface it) throws IllegalArgumentException, IllegalStateException, IOException { assert(name != null && it != null); xml.startTag(null, name); From 17ec357c1b9015020a28857b63ca1dddd5e4a825 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 2 Jun 2019 02:41:51 +0200 Subject: [PATCH 115/289] Kotlinized PwNode PwEntry PwGroup --- .../keepass/tests/PwEntryTestV4.java | 2 +- .../keepass/database/ExtraFields.java | 1 + .../database/cursor/EntryCursorV3.java | 4 +- .../database/cursor/EntryCursorV4.java | 4 +- .../keepass/database/element/Database.kt | 28 +- .../database/element/EntryVersioned.kt | 45 +- .../database/element/GroupVersioned.kt | 26 +- .../database/element/PwDatabaseV4.java | 12 +- .../database/element/PwEntryInterface.kt | 2 - .../keepass/database/element/PwEntryV3.java | 299 ----------- .../keepass/database/element/PwEntryV3.kt | 204 +++++++ .../keepass/database/element/PwEntryV4.java | 503 ------------------ .../keepass/database/element/PwEntryV4.kt | 325 +++++++++++ .../keepass/database/element/PwGroupV3.java | 125 ----- .../keepass/database/element/PwGroupV3.kt | 95 ++++ .../keepass/database/element/PwGroupV4.java | 267 ---------- .../keepass/database/element/PwGroupV4.kt | 137 +++++ .../keepass/database/element/PwIcon.kt | 6 +- .../keepass/database/element/PwIconCustom.kt | 18 +- .../database/element/PwIconStandard.kt | 15 +- .../keepass/database/element/PwNode.kt | 85 ++- .../database/element/PwNodeInterface.kt | 2 + .../keepass/database/load/ImporterV4.java | 11 +- .../keepass/database/save/PwDbV4Output.java | 8 +- .../database/save/PwEntryOutputV3.java | 4 +- .../database/search/EntrySearchHandlerV4.java | 2 +- 26 files changed, 917 insertions(+), 1313 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.kt diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java index f8b0fba06..3dc3c2718 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java @@ -45,7 +45,7 @@ public class PwEntryTestV4 extends TestCase { entry.getAutoType().obfuscationOptions = 123412432109L; entry.getAutoType().put("key", "value"); - entry.setBackgroupColor("blue"); + entry.setBackgroundColor("blue"); entry.putProtectedBinary("key1", new ProtectedBinary(false, new byte[] {0,1})); entry.setIconCustom(new PwIconCustom(UUID.randomUUID(), new byte[0])); entry.setForegroundColor("red"); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java index 184f806f3..2bb63ca5f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.database; import android.os.Parcel; import android.os.Parcelable; + import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.utils.MemUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java index 44eb1b98e..839ed1c0a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java @@ -7,8 +7,8 @@ public class EntryCursorV3 extends EntryCursor { public void addEntry(PwEntryV3 entry) { addRow(new Object[] {entryId, - entry.getNodeId().getId().getMostSignificantBits(), - entry.getNodeId().getId().getLeastSignificantBits(), + entry.getId().getMostSignificantBits(), + entry.getId().getLeastSignificantBits(), entry.getTitle(), entry.getIcon().getIconId(), PwDatabase.UUID_ZERO.getMostSignificantBits(), diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java index e1a71ca40..e2eb4033c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java @@ -17,8 +17,8 @@ public class EntryCursorV4 extends EntryCursor { public void addEntry(PwEntryV4 entry) { addRow(new Object[] {entryId, - entry.getNodeId().getId().getMostSignificantBits(), - entry.getNodeId().getId().getLeastSignificantBits(), + entry.getId().getMostSignificantBits(), + entry.getId().getLeastSignificantBits(), entry.getTitle(), entry.getIcon().getIconId(), entry.getIconCustom().getUuid().getMostSignificantBits(), diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 99dfeaf46..0bd1b7742 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -64,9 +64,9 @@ class Database { var loaded = false - val iconFactory: PwIconFactory? + val iconFactory: PwIconFactory get() { - return pwDatabaseV3?.getIconFactory() ?: pwDatabaseV4?.getIconFactory() + return pwDatabaseV3?.getIconFactory() ?: pwDatabaseV4?.getIconFactory() ?: PwIconFactory() } val name: String @@ -189,7 +189,7 @@ class Database { val databaseV4 = PwDatabaseV4() val groupV4 = databaseV4.createGroup() groupV4.title = dbNameFromPath(databasePath) - groupV4.setIconStandard(databaseV4.getIconFactory().folderIcon) + groupV4.icon = databaseV4.getIconFactory().folderIcon databaseV4.rootGroup = groupV4 setDatabaseV4(databaseV4) } @@ -499,7 +499,14 @@ class Database { } fun createEntry(): EntryVersioned? { - pwDatabaseV3 ?: pwDatabaseV4?.let { database -> + pwDatabaseV3?.let { database -> + return EntryVersioned(database.createEntry()).apply { + database.newEntryId()?.let { + nodeId = it + } + } + } + pwDatabaseV4?.let { database -> return EntryVersioned(database.createEntry()).apply { database.newEntryId()?.let { nodeId = it @@ -511,7 +518,14 @@ class Database { } fun createGroup(): GroupVersioned? { - pwDatabaseV3 ?: pwDatabaseV4?.let { database -> + pwDatabaseV3?.let { database -> + return GroupVersioned(database.createGroup()).apply { + database.newGroupId()?.let { + setNodeId(it) + } + } + } + pwDatabaseV4?.let { database -> return GroupVersioned(database.createGroup()).apply { database.newGroupId()?.let { setNodeId(it) @@ -545,21 +559,25 @@ class Database { fun addEntryTo(entry: EntryVersioned, parent: GroupVersioned) { pwDatabaseV3?.addEntryTo(entry.pwEntryV3, parent.pwGroupV3) pwDatabaseV4?.addEntryTo(entry.pwEntryV4, parent.pwGroupV4) + entry.afterAssignNewParent() } fun removeEntryFrom(entry: EntryVersioned, parent: GroupVersioned) { pwDatabaseV3?.removeEntryFrom(entry.pwEntryV3, parent.pwGroupV3) pwDatabaseV4?.removeEntryFrom(entry.pwEntryV4, parent.pwGroupV4) + entry.afterAssignNewParent() } fun addGroupTo(group: GroupVersioned, parent: GroupVersioned) { pwDatabaseV3?.addGroupTo(group.pwGroupV3, parent.pwGroupV3) pwDatabaseV4?.addGroupTo(group.pwGroupV4, parent.pwGroupV4) + group.afterAssignNewParent() } fun removeGroupFrom(group: GroupVersioned, parent: GroupVersioned) { pwDatabaseV3?.removeGroupFrom(group.pwGroupV3, parent.pwGroupV3) pwDatabaseV4?.removeGroupFrom(group.pwGroupV4, parent.pwGroupV4) + group.afterAssignNewParent() } /** 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 56fa6c22c..1c6fef28c 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 @@ -15,8 +15,12 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { private set fun updateWith(entry: EntryVersioned) { - this.pwEntryV3?.updateWith(entry.pwEntryV3) - this.pwEntryV4?.updateWith(entry.pwEntryV4) + entry.pwEntryV3?.let { + this.pwEntryV3?.updateWith(it) + } + entry.pwEntryV4?.let { + this.pwEntryV4?.updateWith(it) + } } /** @@ -71,7 +75,16 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { } override var icon: PwIcon - get() = pwEntryV3?.icon ?: pwEntryV4?.icon ?: PwIconStandard() + get() { + var iconGet: PwIcon? = pwEntryV3?.icon + if (iconGet == null) + iconGet = pwEntryV4?.iconCustom + if (iconGet == null || iconGet.isUnknown) + iconGet = pwEntryV4?.icon + if (iconGet == null) + PwIconStandard() + return iconGet!! + } set(value) { pwEntryV3?.icon = value pwEntryV4?.icon = value @@ -99,13 +112,24 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { return pwEntryV3?.containsParent() ?: pwEntryV4?.containsParent() ?: false } + override fun afterAssignNewParent() { + pwEntryV4?.afterChangeParent() + } + override fun touch(modified: Boolean, touchParents: Boolean) { pwEntryV3?.touch(modified, touchParents) pwEntryV4?.touch(modified, touchParents) } override fun isContainedIn(container: GroupVersioned): Boolean { - return pwEntryV3?.isContainedIn(container.pwGroupV3) ?: pwEntryV4?.isContainedIn(container.pwGroupV4) ?: false + var contained: Boolean? = false + container.pwGroupV3?.let { + contained = pwEntryV3?.isContainedIn(it) + } + container.pwGroupV4?.let { + contained = pwEntryV4?.isContainedIn(it) + } + return contained ?: false } override val isSearchingEnabled: Boolean @@ -174,11 +198,6 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { pwEntryV4?.notes = value } - override fun touchLocation() { - pwEntryV3?.touchLocation() - pwEntryV4?.touchLocation() - } - private fun isTan(): Boolean { return title == PMS_TAN_ENTRY && username.isNotEmpty() } @@ -210,6 +229,12 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { ------------ */ + var iconCustom: PwIconCustom + get() = pwEntryV4?.iconCustom ?: PwIconCustom.ZERO + set(value) { + pwEntryV4?.iconCustom = value + } + /** * Retrieve extra fields to show, key is the label, value is the value of field * @return Map of label/value @@ -308,7 +333,7 @@ class EntryVersioned : NodeVersioned, PwEntryInterface { return arrayOfNulls(size) } - val PMS_TAN_ENTRY = "" + const val PMS_TAN_ENTRY = "" /** * {@inheritDoc} 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 55ebbe8ca..5e8e7e510 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 @@ -13,8 +13,12 @@ class GroupVersioned : NodeVersioned, PwGroupInterface { PwGroupV4 recycleBin = createGroup(); recycleBin.setTitle(RECYCLEBIN_NAME); - recycleBin.setIconStandard(iconFactory.getTrashIcon()); + recycleBin.setIcon(iconFactory.getTrashIcon()); recycleBin.setEnableAutoType(false); recycleBin.setEnableSearching(false); recycleBin.setExpanded(false); addGroupTo(recycleBin, rootGroup); - recycleBinUUID = recycleBin.getNodeId().getId(); + recycleBinUUID = recycleBin.getId(); } } @@ -601,7 +601,7 @@ public class PwDatabaseV4 extends PwDatabase { addGroupTo(group, getRecycleBin()); - // TODO ? group.touchLocation(); + // TODO ? group.afterChangeParent(); } public void recycle(PwEntryV4 entry) { @@ -611,7 +611,7 @@ public class PwDatabaseV4 extends PwDatabase { addEntryTo(entry, getRecycleBin()); - entry.touchLocation(); + entry.afterChangeParent(); } public void undoRecycle(PwGroupV4 group, PwGroupV4 origParent) { @@ -639,13 +639,13 @@ public class PwDatabaseV4 extends PwDatabase { @Override public void removeEntryFrom(PwEntryV4 entryToRemove, PwGroupV4 parent) { super.removeEntryFrom(entryToRemove, parent); - deletedObjects.add(new PwDeletedObject(entryToRemove.getNodeId().getId())); + deletedObjects.add(new PwDeletedObject(entryToRemove.getId())); } @Override public void undoDeleteEntryFrom(PwEntryV4 entry, PwGroupV4 origParent) { super.undoDeleteEntryFrom(entry, origParent); - deletedObjects.remove(new PwDeletedObject(entry.getNodeId().getId())); + deletedObjects.remove(new PwDeletedObject(entry.getId())); } public PwGroupV4 getRecycleBin() { // TODO delete recycle bin preference diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt index ea6c72a4b..ba9ea4012 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryInterface.kt @@ -9,6 +9,4 @@ interface PwEntryInterface : PwNodeInterface { var url: String var notes: String - - fun touchLocation() } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java deleted file mode 100644 index a701ea8f3..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV3.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - -This file was derived from - -Copyright 2007 Naomaru Itoi - -This file was derived from - -Java clone of KeePass - A KeePass file viewer for Java -Copyright 2006 Bill Zwicky - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; version 2 - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; -import android.support.annotation.NonNull; - -import java.io.UnsupportedEncodingException; -import java.util.Arrays; -import java.util.UUID; - - -/** - * Structure containing information about one entry. - * - *
- * One entry: [FIELDTYPE(FT)][FIELDSIZE(FS)][FIELDDATA(FD)]
- *            [FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)]...
- *            
- * [ 2 bytes] FIELDTYPE
- * [ 4 bytes] FIELDSIZE, size of FIELDDATA in bytes
- * [ n bytes] FIELDDATA, n = FIELDSIZE
- * 
- * Notes:
- *  - Strings are stored in UTF-8 encoded form and are null-terminated.
- *  - FIELDTYPE can be one of the FT_ constants.
- * 
- * - * @author Naomaru Itoi - * @author Bill Zwicky - * @author Dominik Reichl - * @author Jeremy Jamet - */ -public class PwEntryV3 extends PwEntry { - - /** Size of byte buffer needed to hold this struct. */ - private static final String PMS_ID_BINDESC = "bin-stream"; - private static final String PMS_ID_TITLE = "Meta-Info"; - private static final String PMS_ID_USER = "SYSTEM"; - private static final String PMS_ID_URL = "$"; - - private String title = ""; - private String username = ""; - private byte[] password = new byte[0]; - private String url = ""; - private String additional = ""; - /** A string describing what is in pBinaryData */ - private String binaryDesc = ""; - private byte[] binaryData = new byte[0]; - - @NonNull - @Override - protected PwNodeId initNodeId() { - return new PwNodeIdUUID(); - } - - @NonNull - @Override - protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { - return new PwNodeIdUUID(nodeId.getId()); - } - - public PwEntryV3() { - super(); - } - - public PwEntryV3(Parcel parcel) { - super(parcel); - title = parcel.readString(); - username = parcel.readString(); - parcel.readByteArray(password); - url = parcel.readString(); - additional = parcel.readString(); - binaryDesc = parcel.readString(); - parcel.readByteArray(binaryData); - } - - @Override - protected PwGroupV3 readParentParcelable(Parcel parcel) { - return parcel.readParcelable(PwGroupV3.class.getClassLoader()); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeString(title); - dest.writeString(username); - dest.writeByteArray(password); - dest.writeString(url); - dest.writeString(additional); - dest.writeString(binaryDesc); - dest.writeByteArray(binaryData); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwEntryV3 createFromParcel(Parcel in) { - return new PwEntryV3(in); - } - - @Override - public PwEntryV3[] newArray(int size) { - return new PwEntryV3[size]; - } - }; - - public void updateWith(PwEntryV3 source) { - super.updateWith(source); - title = source.title; - username = source.username; - - if (source.password != null) { - int passLen = source.password.length; - password = new byte[passLen]; - System.arraycopy(source.password, 0, password, 0, passLen); - } - - url = source.url; - additional = source.additional; - binaryDesc = source.binaryDesc; - - if ( source.binaryData != null ) { - int descLen = source.binaryData.length; - binaryData = new byte[descLen]; - System.arraycopy(source.binaryData, 0, binaryData, 0, descLen); - } - } - - @Override - public Type getType() { - return Type.ENTRY; - } - - @Override - public String getUsername() { - return username; - } - - @Override - public void setUsername(String user) { - username = user; - } - - @Override - public String getTitle() { - return title; - } - - @Override - public void setTitle(String title) { - this.title = title; - } - - @Override - public String getNotes() { - return additional; - } - - @Override - public void setNotes(String notes) { - additional = notes; - } - - @Override - public String getUrl() { - return url; - } - - @Override - public void setUrl(String url) { - this.url = url; - } - - /** - * @return the actual password byte array. - */ - @Override - public String getPassword() { - return new String(password); - } - - public byte[] getPasswordBytes() { - return password; - } - - /** - * fill byte array - */ - private static void fill(byte[] array, byte value) { - for (int i=0; i. + */ + +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import java.io.UnsupportedEncodingException +import java.util.Arrays +import java.util.UUID + + +/** + * Structure containing information about one entry. + * + *
+ * One entry: [FIELDTYPE(FT)][FIELDSIZE(FS)][FIELDDATA(FD)]
+ * [FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)]...
+ *
+ * [ 2 bytes] FIELDTYPE
+ * [ 4 bytes] FIELDSIZE, size of FIELDDATA in bytes
+ * [ n bytes] FIELDDATA, n = FIELDSIZE
+ *
+ * Notes:
+ * - Strings are stored in UTF-8 encoded form and are null-terminated.
+ * - FIELDTYPE can be one of the FT_ constants.
+
* + * + * @author Naomaru Itoi @phoneid.org> + * @author Bill Zwicky @pobox.com> + * @author Dominik Reichl @t-online.de> + * @author Jeremy Jamet @kunzisoft.com> + */ +class PwEntryV3 : PwEntry { + + /** A string describing what is in pBinaryData */ + var binaryDesc = "" + /** + * @return the actual binaryData byte array. + */ + var binaryData: ByteArray = ByteArray(0) + private set + + // Determine if this is a MetaStream entry + val isMetaStream: Boolean + get() { + if (Arrays.equals(binaryData, ByteArray(0))) return false + if (notes.isEmpty()) return false + if (binaryDesc != PMS_ID_BINDESC) return false + if (title.isEmpty()) return false + if (title != PMS_ID_TITLE) return false + if (username.isEmpty()) return false + if (username != PMS_ID_USER) return false + if (url.isEmpty()) return false + return if (url != PMS_ID_URL) false else icon.isMetaStreamIcon + } + + override fun initNodeId(): PwNodeId { + return PwNodeIdUUID() + } + + override fun copyNodeId(nodeId: PwNodeId): PwNodeId { + return PwNodeIdUUID(nodeId.id) + } + + constructor() : super() + + constructor(parcel: Parcel) : super(parcel) { + title = parcel.readString() + username = parcel.readString() + parcel.readByteArray(passwordBytes) + url = parcel.readString() + notes = parcel.readString() + binaryDesc = parcel.readString() + parcel.readByteArray(binaryData) + } + + override fun readParentParcelable(parcel: Parcel): PwGroupV3 { + return parcel.readParcelable(PwGroupV3::class.java.classLoader) + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeString(title) + dest.writeString(username) + dest.writeByteArray(passwordBytes) + dest.writeString(url) + dest.writeString(notes) + dest.writeString(binaryDesc) + dest.writeByteArray(binaryData) + } + + fun updateWith(source: PwEntryV3) { + super.updateWith(source) + title = source.title + username = source.username + + val passLen = source.passwordBytes.size + passwordBytes = ByteArray(passLen) + System.arraycopy(source.passwordBytes, 0, passwordBytes, 0, passLen) + + url = source.url + notes = source.notes + binaryDesc = source.binaryDesc + + val descLen = source.binaryData.size + binaryData = ByteArray(descLen) + System.arraycopy(source.binaryData, 0, binaryData, 0, descLen) + } + + override var username = "" + + var passwordBytes: ByteArray = ByteArray(0) + private set + + /** Securely erase old password before copying new. */ + fun setPassword(buf: ByteArray, offset: Int, len: Int) { + fill(passwordBytes, 0.toByte()) + passwordBytes = ByteArray(len) + System.arraycopy(buf, offset, passwordBytes, 0, len) + } + + /** + * @return the actual password byte array. + */ + override var password: String + get() = String(passwordBytes) + set(pass) { + var password: ByteArray + try { + password = pass.toByteArray(charset("UTF-8")) + setPassword(password, 0, password.size) + } catch (e: UnsupportedEncodingException) { + password = pass.toByteArray() + setPassword(password, 0, password.size) + } + + } + + override var url = "" + + override var notes = "" + + override var title = "" + + override val type: Type + get() = Type.ENTRY + + override val isSearchingEnabled: Boolean + get() = false + + + fun setBinaryData(buf: ByteArray, offset: Int, len: Int) { + /** Securely erase old data before copying new. */ + fill(binaryData, 0.toByte()) + binaryData = ByteArray(len) + System.arraycopy(buf, offset, binaryData, 0, len) + } + + companion object { + + /** Size of byte buffer needed to hold this struct. */ + private const val PMS_ID_BINDESC = "bin-stream" + private const val PMS_ID_TITLE = "Meta-Info" + private const val PMS_ID_USER = "SYSTEM" + private const val PMS_ID_URL = "$" + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(`in`: Parcel): PwEntryV3 { + return PwEntryV3(`in`) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + /** + * fill byte array + */ + private fun fill(array: ByteArray, value: Byte) { + for (i in array.indices) + array[i] = value + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java deleted file mode 100644 index c99d401b7..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.java +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; - -import android.support.annotation.NonNull; -import com.kunzisoft.keepass.database.AutoType; -import com.kunzisoft.keepass.database.ExtraFields; -import com.kunzisoft.keepass.database.security.ProtectedBinary; -import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.utils.MemUtil; -import com.kunzisoft.keepass.utils.SprEngineV4; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; - -public class PwEntryV4 extends PwEntry implements NodeV4Interface { - - public static final String STR_TITLE = "Title"; - public static final String STR_USERNAME = "UserName"; - public static final String STR_PASSWORD = "Password"; - public static final String STR_URL = "URL"; - public static final String STR_NOTES = "Notes"; - - // To decode each field not parcelable - private transient PwDatabaseV4 mDatabase = null; - private transient boolean mDecodeRef = false; - - private PwIconCustom customIcon = PwIconCustom.Companion.getZERO(); - private long usageCount = 0; - private PwDate parentGroupLastMod = new PwDate(); - private Map customData = new HashMap<>(); - private ExtraFields fields = new ExtraFields(); - private HashMap binaries = new HashMap<>(); - private String foregroundColor = ""; - private String backgroupColor = ""; - private String overrideURL = ""; - private AutoType autoType = new AutoType(); - private ArrayList history = new ArrayList<>(); - private String url = ""; - private String additional = ""; - private String tags = ""; - - @NonNull - @Override - protected PwNodeId initNodeId() { - return new PwNodeIdUUID(); - } - - @NonNull - @Override - protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { - return new PwNodeIdUUID(nodeId.getId()); - } - - public PwEntryV4() { - super(); - } - - public PwEntryV4(Parcel parcel) { - super(parcel); - customIcon = parcel.readParcelable(PwIconCustom.class.getClassLoader()); - usageCount = parcel.readLong(); - parentGroupLastMod = parcel.readParcelable(PwDate.class.getClassLoader()); - customData = MemUtil.readStringParcelableMap(parcel); - fields = parcel.readParcelable(ExtraFields.class.getClassLoader()); - // TODO binaries = MemUtil.readStringParcelableMap(parcel, ProtectedBinary.class); - foregroundColor = parcel.readString(); - backgroupColor = parcel.readString(); - overrideURL = parcel.readString(); - autoType = parcel.readParcelable(AutoType.class.getClassLoader()); - history = parcel.readArrayList(PwEntryV4.class.getClassLoader()); // TODO verify - url = parcel.readString(); - additional = parcel.readString(); - tags = parcel.readString(); - } - - @Override - protected PwGroupV4 readParentParcelable(Parcel parcel) { - return parcel.readParcelable(PwGroupV4.class.getClassLoader()); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeParcelable(customIcon, flags); - dest.writeLong(usageCount); - dest.writeParcelable(parentGroupLastMod, flags); - MemUtil.writeStringParcelableMap(dest, customData); - dest.writeParcelable(fields, flags); - // TODO MemUtil.writeStringParcelableMap(dest, flags, binaries); - dest.writeString(foregroundColor); - dest.writeString(backgroupColor); - dest.writeString(overrideURL); - dest.writeParcelable(autoType, flags); - dest.writeList(history); - dest.writeString(url); - dest.writeString(additional); - dest.writeString(tags); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwEntryV4 createFromParcel(Parcel in) { - return new PwEntryV4(in); - } - - @Override - public PwEntryV4[] newArray(int size) { - return new PwEntryV4[size]; - } - }; - - /** - * Update with deep copy of each entry element - * @param source - */ - public void updateWith(PwEntryV4 source) { - super.updateWith(source); - customIcon = new PwIconCustom(source.customIcon); - usageCount = source.usageCount; - parentGroupLastMod = new PwDate(source.parentGroupLastMod); - // Add all custom elements in map - customData.clear(); - for (Map.Entry entry : source.customData.entrySet()) { - customData.put(entry.getKey(), entry.getValue()); - } - fields = new ExtraFields(source.fields); - for (Map.Entry entry: source.binaries.entrySet()) { - binaries.put(entry.getKey(), new ProtectedBinary(entry.getValue())); - } - foregroundColor = source.foregroundColor; - backgroupColor = source.backgroupColor; - overrideURL = source.overrideURL; - autoType = new AutoType(source.autoType); - history.clear(); - history.addAll(source.history); - url = source.url; - additional = source.additional; - tags = source.tags; - } - - @NonNull - @Override - public Type getType() { - return Type.ENTRY; - } - - public void startToManageFieldReferences(PwDatabaseV4 db) { - this.mDatabase = db; - this.mDecodeRef = true; - } - - public void stopToManageFieldReferences() { - this.mDatabase = null; - this.mDecodeRef = false; - } - - /** - * Decode a reference key woth the SprEngineV4 - * @param decodeRef - * @param key - * @return - */ - private String decodeRefKey(boolean decodeRef, String key) { - String text = getProtectedStringValue(key); - if (decodeRef) { - if (mDatabase == null) - return text; - return new SprEngineV4().compile(text, this, mDatabase); - } - return text; - } - - @NonNull - @Override - public String getUsername() { - return decodeRefKey(mDecodeRef, STR_USERNAME); - } - - @NonNull - @Override - public String getTitle() { - return decodeRefKey(mDecodeRef, STR_TITLE); - } - - @NonNull - @Override - public String getPassword() { - return decodeRefKey(mDecodeRef, STR_PASSWORD); - } - - @Override - public void setTitle(@NonNull String title) { - boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectTitle; - setProtectedString(STR_TITLE, title, protect); - } - - @Override - public void setUsername(@NonNull String user) { - boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectUserName; - setProtectedString(STR_USERNAME, user, protect); - } - - @Override - public void setPassword(@NonNull String pass) { - boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectPassword; - setProtectedString(STR_PASSWORD, pass, protect); - } - - @Override - public void setUrl(@NonNull String url) { - boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectUrl; - setProtectedString(STR_URL, url, protect); - } - - @Override - public void setNotes(@NonNull String notes) { - boolean protect = (mDatabase != null) && mDatabase.getMemoryProtection().protectNotes; - setProtectedString(STR_NOTES, notes, protect); - } - - public String getProtectedStringValue(String key) { - return fields.getProtectedStringValue(key); - } - - public void setProtectedString(String key, String value, boolean protect) { - fields.putProtectedString(key, value, protect); - } - - public PwDate getLocationChanged() { - return parentGroupLastMod; - } - - public long getUsageCount() { - return usageCount; - } - - public void setLocationChanged(PwDate date) { - parentGroupLastMod = date; - } - - public void setUsageCount(long count) { - usageCount = count; - } - - @NonNull - @Override - public String getNotes() { - return decodeRefKey(mDecodeRef, STR_NOTES); - } - - @NonNull - @Override - public String getUrl() { - return decodeRefKey(mDecodeRef, STR_URL); - } - - @NonNull - @Override - public PwIcon getIcon() { - if (customIcon == null || customIcon.isUnknown()) { - return super.getIcon(); - } else { - return customIcon; - } - } - - @Override - public void setIcon(@NonNull PwIcon icon) { - if (icon instanceof PwIconStandard) - setIconStandard((PwIconStandard) icon); - if (icon instanceof PwIconCustom) - setIconCustom((PwIconCustom) icon); - } - - public PwIconCustom getIconCustom() { - return customIcon; - } - - public void setIconCustom(PwIconCustom icon) { - this.customIcon = icon; - } - - public PwIcon getIconStandard() { - return getIcon(); - } - - public void setIconStandard(PwIconStandard icon) { - super.setIcon(icon); - this.customIcon = PwIconCustom.Companion.getZERO(); - } - - public boolean allowExtraFields() { - return true; - } - - @NonNull - public ExtraFields getFields() { - return fields; - } - - public boolean containsCustomFields() { - return getFields().containsCustomFields(); - } - - public boolean containsCustomFieldsProtected() { - return getFields().containsCustomFieldsProtected(); - } - - public boolean containsCustomFieldsNotProtected() { - return getFields().containsCustomFieldsNotProtected(); - } - - public void addExtraField(String label, ProtectedString value) { - fields.putProtectedString(label, value); - } - - public void removeAllCustomFields() { - fields.removeAllCustomFields(); - } - - public HashMap getBinaries() { - return binaries; - } - - public void putProtectedBinary(String key, ProtectedBinary value) { - binaries.put(key, value); - } - - public String getForegroundColor() { - return foregroundColor; - } - - public void setForegroundColor(String color) { - this.foregroundColor = color; - } - - public String getBackgroupColor() { - return backgroupColor; - } - - public void setBackgroupColor(String color) { - this.backgroupColor = color; - } - - public String getOverrideURL() { - return overrideURL; - } - - public void setOverrideURL(String overrideURL) { - this.overrideURL = overrideURL; - } - - public AutoType getAutoType() { - return autoType; - } - - public void setAutoType(AutoType autoType) { - this.autoType = autoType; - } - - public ArrayList getHistory() { - return history; - } - - public void setHistory(ArrayList history) { - this.history = history; - } - - public void addToHistory(PwEntryV4 entry) { - history.add(entry); - } - - public int sizeOfHistory() { - return history.size(); - } - - public String getAdditional() { - return additional; - } - - public void setAdditional(String additional) { - this.additional = additional; - } - - public String getTags() { - return tags; - } - - public void setTags(String tags) { - this.tags = tags; - } - - public void putCustomData(String key, String value) { - customData.put(key, value); - } - - public boolean containsCustomData() { - return customData.size() > 0; - } - - private static final long FIXED_LENGTH_SIZE = 128; // Approximate fixed length size - public long getSize() { - long size = FIXED_LENGTH_SIZE; - - for (Entry pair : fields.getListOfAllFields().entrySet()) { - size += pair.getKey().length(); - size += pair.getValue().length(); - } - - for (Entry pair : binaries.entrySet()) { - size += pair.getKey().length(); - size += pair.getValue().length(); - } - - size += autoType.defaultSequence.length(); - for (Entry pair : autoType.entrySet()) { - size += pair.getKey().length(); - size += pair.getValue().length(); - } - - for (PwEntryV4 entry : history) { - size += entry.getSize(); - } - - size += overrideURL.length(); - size += tags.length(); - - return size; - } - - public void addEntryToHistory(PwEntryV4 entry) { - history.add(entry); - } - - public void removeOldestEntryFromHistory() { - Date min = null; - int index = -1; - - for (int i = 0; i < history.size(); i++) { - PwEntryV4 entry = history.get(i); - Date lastMod = entry.getLastModificationTime().getDate(); - if ((min == null) || lastMod.before(min)) { - index = i; - min = lastMod; - } - } - - if (index != -1) { - history.remove(index); - } - } - - @Override - public void touch(boolean modified, boolean touchParents) { - super.touch(modified, touchParents); - ++usageCount; - } - - @Override - public void touchLocation() { - parentGroupLastMod = new PwDate(); - } - - @Override - public boolean isSearchingEnabled() { - if (getParent() != null) { - return getParent().isSearchingEnabled(); - } - return true; - } - - /** - * If it's a node with only meta information like Meta-info SYSTEM Database Color - * @return false by default, true if it's a meta stream - */ - public boolean isMetaStream() { - return false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt new file mode 100644 index 000000000..a35cca539 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt @@ -0,0 +1,325 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable +import com.kunzisoft.keepass.database.AutoType +import com.kunzisoft.keepass.database.ExtraFields +import com.kunzisoft.keepass.database.security.ProtectedBinary +import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.utils.MemUtil +import com.kunzisoft.keepass.utils.SprEngineV4 +import java.util.* + +class PwEntryV4 : PwEntry, NodeV4Interface { + + // To decode each field not parcelable + @Transient + private var mDatabase: PwDatabaseV4? = null + @Transient + private var mDecodeRef = false + + var iconCustom = PwIconCustom.ZERO + private var customData = HashMap() + var fields = ExtraFields() + private set + val binaries = HashMap() + var foregroundColor = "" + var backgroundColor = "" + var overrideURL = "" + var autoType = AutoType() + var history = ArrayList() + var additional = "" + var tags = "" + + val size: Long + get() { + var size = FIXED_LENGTH_SIZE + + for ((key, value) in fields.listOfAllFields) { + size += key.length.toLong() + size += value.length().toLong() + } + + for ((key, value) in binaries) { + size += key.length.toLong() + size += value.length() + } + + size += autoType.defaultSequence.length.toLong() + for ((key, value) in autoType.entrySet()) { + size += key.length.toLong() + size += value.length.toLong() + } + + for (entry in history) { + size += entry.size + } + + size += overrideURL.length.toLong() + size += tags.length.toLong() + + return size + } + + constructor() : super() + + constructor(parcel: Parcel) : super(parcel) { + iconCustom = parcel.readParcelable(PwIconCustom::class.java.classLoader) + usageCount = parcel.readLong() + locationChanged = parcel.readParcelable(PwDate::class.java.classLoader) + customData = MemUtil.readStringParcelableMap(parcel) + fields = parcel.readParcelable(ExtraFields::class.java.classLoader) + // TODO binaries = MemUtil.readStringParcelableMap(parcel, ProtectedBinary.class); + foregroundColor = parcel.readString() + backgroundColor = parcel.readString() + overrideURL = parcel.readString() + autoType = parcel.readParcelable(AutoType::class.java.classLoader) + parcel.readTypedList(history, CREATOR) + url = parcel.readString() + additional = parcel.readString() + tags = parcel.readString() + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeParcelable(iconCustom, flags) + dest.writeLong(usageCount) + dest.writeParcelable(locationChanged, flags) + MemUtil.writeStringParcelableMap(dest, customData) + dest.writeParcelable(fields, flags) + // TODO MemUtil.writeStringParcelableMap(dest, flags, binaries); + dest.writeString(foregroundColor) + dest.writeString(backgroundColor) + dest.writeString(overrideURL) + dest.writeParcelable(autoType, flags) + dest.writeTypedList(history) + dest.writeString(url) + dest.writeString(additional) + dest.writeString(tags) + } + + /** + * Update with deep copy of each entry element + * @param source + */ + fun updateWith(source: PwEntryV4) { + super.updateWith(source) + iconCustom = PwIconCustom(source.iconCustom) + usageCount = source.usageCount + locationChanged = PwDate(source.locationChanged) + // Add all custom elements in map + customData.clear() + for ((key, value) in source.customData) { + customData[key] = value + } + fields = ExtraFields(source.fields) + for ((key, value) in source.binaries) { + binaries[key] = ProtectedBinary(value) + } + foregroundColor = source.foregroundColor + backgroundColor = source.backgroundColor + overrideURL = source.overrideURL + autoType = AutoType(source.autoType) + history.clear() + history.addAll(source.history) + url = source.url + additional = source.additional + tags = source.tags + } + + fun startToManageFieldReferences(db: PwDatabaseV4) { + this.mDatabase = db + this.mDecodeRef = true + } + + fun stopToManageFieldReferences() { + this.mDatabase = null + this.mDecodeRef = false + } + + override fun initNodeId(): PwNodeId { + return PwNodeIdUUID() + } + + override fun copyNodeId(nodeId: PwNodeId): PwNodeId { + return PwNodeIdUUID(nodeId.id) + } + + override fun readParentParcelable(parcel: Parcel): PwGroupV4 { + return parcel.readParcelable(PwGroupV4::class.java.classLoader) + } + + /** + * Decode a reference key woth the SprEngineV4 + * @param decodeRef + * @param key + * @return + */ + private fun decodeRefKey(decodeRef: Boolean, key: String): String { + val text = fields.getProtectedStringValue(key) + return if (decodeRef) { + if (mDatabase == null) text else SprEngineV4().compile(text, this, mDatabase) + } else text + } + + override var title: String + get() = decodeRefKey(mDecodeRef, STR_TITLE) + set(value) { + val protect = mDatabase != null && mDatabase!!.memoryProtection.protectTitle + fields.putProtectedString(STR_TITLE, value, protect) + } + + override val type: Type + get() = Type.ENTRY + + override val isSearchingEnabled: Boolean + get() = parent?.isSearchingEnabled ?: true + + override var username: String + get() = decodeRefKey(mDecodeRef, STR_USERNAME) + set(value) { + val protect = mDatabase != null && mDatabase!!.memoryProtection.protectUserName + fields.putProtectedString(STR_USERNAME, value, protect) + } + + override var password: String + get() = decodeRefKey(mDecodeRef, STR_PASSWORD) + set(value) { + val protect = mDatabase != null && mDatabase!!.memoryProtection.protectPassword + fields.putProtectedString(STR_PASSWORD, value, protect) + } + + override var url + get() = decodeRefKey(mDecodeRef, STR_URL) + set(value) { + val protect = mDatabase != null && mDatabase!!.memoryProtection.protectUrl + fields.putProtectedString(STR_URL, value, protect) + } + + override var notes: String + get() = decodeRefKey(mDecodeRef, STR_NOTES) + set(value) { + val protect = mDatabase != null && mDatabase!!.memoryProtection.protectNotes + fields.putProtectedString(STR_NOTES, value, protect) + } + + override var usageCount: Long = 0 + + override var locationChanged = PwDate() + + fun afterChangeParent() { + locationChanged = PwDate() + } + + fun allowExtraFields(): Boolean { + return true + } + + fun containsCustomFields(): Boolean { + return fields.containsCustomFields() + } + + fun containsCustomFieldsProtected(): Boolean { + return fields.containsCustomFieldsProtected() + } + + fun containsCustomFieldsNotProtected(): Boolean { + return fields.containsCustomFieldsNotProtected() + } + + fun addExtraField(label: String, value: ProtectedString) { + fields.putProtectedString(label, value) + } + + fun removeAllCustomFields() { + fields.removeAllCustomFields() + } + + fun putProtectedBinary(key: String, value: ProtectedBinary) { + binaries[key] = value + } + + fun addToHistory(entry: PwEntryV4) { + history.add(entry) + } + + fun sizeOfHistory(): Int { + return history.size + } + + fun putCustomData(key: String, value: String) { + customData[key] = value + } + + fun containsCustomData(): Boolean { + return customData.size > 0 + } + + fun addEntryToHistory(entry: PwEntryV4) { + history.add(entry) + } + + fun removeOldestEntryFromHistory() { + var min: Date? = null + var index = -1 + + for (i in history.indices) { + val entry = history[i] + val lastMod = entry.lastModificationTime.date + if (min == null || lastMod.before(min)) { + index = i + min = lastMod + } + } + + if (index != -1) { + history.removeAt(index) + } + } + + override fun touch(modified: Boolean, touchParents: Boolean) { + super.touch(modified, touchParents) + ++usageCount + } + + companion object { + + const val STR_TITLE = "Title" + const val STR_USERNAME = "UserName" + const val STR_PASSWORD = "Password" + const val STR_URL = "URL" + const val STR_NOTES = "Notes" + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PwEntryV4 { + return PwEntryV4(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + private const val FIXED_LENGTH_SIZE: Long = 128 // Approximate fixed length size + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java deleted file mode 100644 index 551ea4657..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * -*/ - -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; -import android.support.annotation.NonNull; - -public class PwGroupV3 extends PwGroup { - - private int level = 0; // short - /** Used by KeePass internally, don't use */ - private int flags; - - @NonNull - @Override - protected PwNodeId initNodeId() { - return new PwNodeIdInt(); - } - - @NonNull - @Override - protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { - return new PwNodeIdInt(nodeId.getId()); - } - - public PwGroupV3() { - super(); - } - - public PwGroupV3(Parcel in) { - super(in); - level = in.readInt(); - flags = in.readInt(); - } - - @Override - protected PwGroupV3 readParentParcelable(Parcel parcel) { - return parcel.readParcelable(PwGroupV3.class.getClassLoader()); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(level); - dest.writeInt(flags); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwGroupV3 createFromParcel(Parcel in) { - return new PwGroupV3(in); - } - - @Override - public PwGroupV3[] newArray(int size) { - return new PwGroupV3[size]; - } - }; - - protected void updateWith(PwGroupV3 source) { - super.updateWith(source); - level = source.level; - flags = source.flags; - } - - @Override - public Type getType() { - return Type.GROUP; - } - - public void setParent(PwGroupV3 parent) { - super.setParent(parent); - try { - level = parent.getLevel() + 1; - } catch (ClassCastException ignored) {} - } - - @Override - public boolean isSearchingEnabled() { - return false; - } - - public void setGroupId(int groupId) { - this.setNodeId(new PwNodeIdInt(groupId)); - } - - public int getLevel() { - return level; - } - - public void setLevel(int level) { - this.level = level; - } - - public int getFlags() { - return flags; - } - - public void setFlags(int flags) { - this.flags = flags; - } - - @Override - public boolean allowAddEntryIfIsRoot() { - return false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.kt new file mode 100644 index 000000000..7cd9dd5f2 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV3.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * +*/ + +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +class PwGroupV3 : PwGroup { + + var level = 0 // short + /** Used by KeePass internally, don't use */ + var flags: Int = 0 + + constructor() : super() + + constructor(parcel: Parcel) : super(parcel) { + level = parcel.readInt() + flags = parcel.readInt() + } + + override fun readParentParcelable(parcel: Parcel): PwGroupV3 { + return parcel.readParcelable(PwGroupV3::class.java.classLoader) + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeInt(level) + dest.writeInt(flags) + } + + fun updateWith(source: PwGroupV3) { + super.updateWith(source) + level = source.level + flags = source.flags + } + + override val type: Type + get() = Type.GROUP + + override val isSearchingEnabled: Boolean + get() = false + + override fun initNodeId(): PwNodeId { + return PwNodeIdInt() + } + + override fun copyNodeId(nodeId: PwNodeId): PwNodeId { + return PwNodeIdInt(nodeId.id) + } + + override fun afterAssignNewParent() { + if (parent != null) + level = parent!!.level + 1 + } + + fun setGroupId(groupId: Int) { + this.nodeId = PwNodeIdInt(groupId) + } + + override fun allowAddEntryIfIsRoot(): Boolean { + return false + } + + companion object { + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PwGroupV3 { + return PwGroupV3(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java deleted file mode 100644 index 220a3a7d0..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; -import android.support.annotation.NonNull; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class PwGroupV4 extends PwGroup implements NodeV4Interface { - - private PwIconCustom customIcon = PwIconCustom.Companion.getZERO(); - private long usageCount = 0; - private PwDate locationChangeDate = new PwDate(); - private Map customData = new HashMap<>(); - private boolean expires = false; - private String notes = ""; - private boolean isExpanded = true; - private String defaultAutoTypeSequence = ""; - private Boolean enableAutoType = null; - private Boolean enableSearching = null; - private UUID lastTopVisibleEntry = PwDatabase.UUID_ZERO; - - @NonNull - @Override - protected PwNodeId initNodeId() { - return new PwNodeIdUUID(); - } - - @NonNull - @Override - protected PwNodeId copyNodeId(@NonNull PwNodeId nodeId) { - return new PwNodeIdUUID(nodeId.getId()); - } - - public PwGroupV4() { - super(); - } - - public PwGroupV4(Parcel in) { - super(in); - customIcon = in.readParcelable(PwIconCustom.class.getClassLoader()); - usageCount = in.readLong(); - locationChangeDate = in.readParcelable(PwDate.class.getClassLoader()); - // TODO customData = MemUtil.readStringParcelableMap(in); - expires = in.readByte() != 0; - notes = in.readString(); - isExpanded = in.readByte() != 0; - defaultAutoTypeSequence = in.readString(); - byte autoTypeByte = in.readByte(); - enableAutoType = (autoTypeByte == -1) ? null : autoTypeByte != 0; - byte enableSearchingByte = in.readByte(); - enableSearching = (enableSearchingByte == -1) ? null : enableSearchingByte != 0; - lastTopVisibleEntry = (UUID) in.readSerializable(); - } - - @Override - protected PwGroupV4 readParentParcelable(Parcel parcel) { - return parcel.readParcelable(PwGroupV4.class.getClassLoader()); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeParcelable(customIcon, flags); - dest.writeLong(usageCount); - dest.writeParcelable(locationChangeDate, flags); - // TODO MemUtil.writeStringParcelableMap(dest, customData); - dest.writeByte((byte) (expires ? 1 : 0)); - dest.writeString(notes); - dest.writeByte((byte) (isExpanded ? 1 : 0)); - dest.writeString(defaultAutoTypeSequence); - dest.writeByte((byte) (enableAutoType == null ? -1 : (enableAutoType ? 1 : 0))); - dest.writeByte((byte) (enableAutoType == null ? -1 : (enableAutoType ? 1 : 0))); - dest.writeSerializable(lastTopVisibleEntry); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwGroupV4 createFromParcel(Parcel in) { - return new PwGroupV4(in); - } - - @Override - public PwGroupV4[] newArray(int size) { - return new PwGroupV4[size]; - } - }; - - protected void updateWith(PwGroupV4 source) { - super.updateWith(source); - customIcon = new PwIconCustom(source.customIcon); - usageCount = source.usageCount; - locationChangeDate = new PwDate(source.locationChangeDate); - // Add all custom elements in map - customData.clear(); - for (Map.Entry entry : source.customData.entrySet()) { - customData.put(entry.getKey(), entry.getValue()); - } - - expires = source.expires; - - notes = source.notes; - isExpanded = source.isExpanded; - defaultAutoTypeSequence = source.defaultAutoTypeSequence; - enableAutoType = source.enableAutoType; - enableSearching = source.enableSearching; - lastTopVisibleEntry = source.lastTopVisibleEntry; - } - - @NonNull - @Override - public Type getType() { - return Type.GROUP; - } - - @Override - public void setParent(PwGroupV4 parent) { - super.setParent(parent); - locationChangeDate = new PwDate(); - } - - @Override - public PwDate getLocationChanged() { - return locationChangeDate; - } - - @Override - public void setLocationChanged(PwDate date) { - locationChangeDate = date; - } - - @Override - public long getUsageCount() { - return usageCount; - } - - @Override - public void setUsageCount(long count) { - usageCount = count; - } - - @Override - public boolean isExpires() { - return expires; - } - - @Override - public void setExpires(boolean exp) { - expires = exp; - } - - @NonNull - @Override - public PwIcon getIcon() { - if (customIcon == null || customIcon.isUnknown()) { // TODO Encapsulate with PwEntryV4 - return super.getIcon(); - } else { - return customIcon; - } - } - - public PwIconCustom getIconCustom() { - return customIcon; - } - - public void setIconCustom(PwIconCustom icon) { - this.customIcon = icon; - } - - public PwIcon getIconStandard() { - return getIcon(); - } - - public void setIconStandard(PwIconStandard icon) { // TODO Encapsulate with PwEntryV4 - super.setIcon(icon); - this.customIcon = PwIconCustom.Companion.getZERO(); - } - - public void putCustomData(String key, String value) { - customData.put(key, value); - } - - public boolean containsCustomData() { - return customData.size() > 0; - } - - public String getNotes() { - return notes; - } - - public void setNotes(String notes) { - this.notes = notes; - } - - public boolean isExpanded() { - return isExpanded; - } - - public void setExpanded(boolean expanded) { - isExpanded = expanded; - } - - public String getDefaultAutoTypeSequence() { - return defaultAutoTypeSequence; - } - - public void setDefaultAutoTypeSequence(String defaultAutoTypeSequence) { - this.defaultAutoTypeSequence = defaultAutoTypeSequence; - } - - public Boolean getEnableAutoType() { - return enableAutoType; - } - - public void setEnableAutoType(Boolean enableAutoType) { - this.enableAutoType = enableAutoType; - } - - public Boolean getEnableSearching() { - return enableSearching; - } - - public void setEnableSearching(Boolean enableSearching) { - this.enableSearching = enableSearching; - } - - public UUID getLastTopVisibleEntry() { - return lastTopVisibleEntry; - } - - public void setLastTopVisibleEntry(UUID lastTopVisibleEntry) { - this.lastTopVisibleEntry = lastTopVisibleEntry; - } - - @Override - public boolean isSearchingEnabled() { - if (getParent() != null) { - return getParent().isSearchingEnabled(); - } - return true; - } - - @Override - public boolean allowAddEntryIfIsRoot() { - return true; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.kt new file mode 100644 index 000000000..e772017d9 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwGroupV4.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import java.util.HashMap +import java.util.UUID + +class PwGroupV4 : PwGroup, NodeV4Interface { + + var iconCustom = PwIconCustom.ZERO + private val customData = HashMap() + var notes = "" + var isExpanded = true + var defaultAutoTypeSequence = "" + var enableAutoType: Boolean? = null + var enableSearching: Boolean? = null + var lastTopVisibleEntry: UUID = PwDatabase.UUID_ZERO + + override val type: Type + get() = Type.GROUP + + override val isSearchingEnabled: Boolean + get() = parent?.isSearchingEnabled ?: true + + override fun initNodeId(): PwNodeId { + return PwNodeIdUUID() + } + + override fun copyNodeId(nodeId: PwNodeId): PwNodeId { + return PwNodeIdUUID(nodeId.id) + } + + constructor() : super() + + constructor(parcel: Parcel) : super(parcel) { + iconCustom = parcel.readParcelable(PwIconCustom::class.java.classLoader) + usageCount = parcel.readLong() + locationChanged = parcel.readParcelable(PwDate::class.java.classLoader) + // TODO customData = MemUtil.readStringParcelableMap(in); + notes = parcel.readString() + isExpanded = parcel.readByte().toInt() != 0 + defaultAutoTypeSequence = parcel.readString() + enableAutoType = parcel.readByte().toInt() != 0 + enableSearching = parcel.readByte().toInt() != 0 + lastTopVisibleEntry = parcel.readSerializable() as UUID + } + + override fun readParentParcelable(parcel: Parcel): PwGroupV4 { + return parcel.readParcelable(PwGroupV4::class.java.classLoader) + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + super.writeToParcel(dest, flags) + dest.writeParcelable(iconCustom, flags) + dest.writeLong(usageCount) + dest.writeParcelable(locationChanged, flags) + // TODO MemUtil.writeStringParcelableMap(dest, customData); + dest.writeString(notes) + dest.writeByte((if (isExpanded) 1 else 0).toByte()) + dest.writeString(defaultAutoTypeSequence) + dest.writeByte((if (enableAutoType == null) -1 else if (enableAutoType!!) 1 else 0).toByte()) + dest.writeByte((if (enableSearching == null) -1 else if (enableSearching!!) 1 else 0).toByte()) + dest.writeSerializable(lastTopVisibleEntry) + } + + fun updateWith(source: PwGroupV4) { + super.updateWith(source) + iconCustom = PwIconCustom(source.iconCustom) + usageCount = source.usageCount + locationChanged = PwDate(source.locationChanged) + // Add all custom elements in map + customData.clear() + for ((key, value) in source.customData) { + customData[key] = value + } + notes = source.notes + isExpanded = source.isExpanded + defaultAutoTypeSequence = source.defaultAutoTypeSequence + enableAutoType = source.enableAutoType + enableSearching = source.enableSearching + lastTopVisibleEntry = source.lastTopVisibleEntry + } + + override var usageCount: Long = 0 + + override var locationChanged = PwDate() + + override fun afterAssignNewParent() { + locationChanged = PwDate() + } + + fun putCustomData(key: String, value: String) { + customData[key] = value + } + + fun containsCustomData(): Boolean { + return customData.size > 0 + } + + override fun allowAddEntryIfIsRoot(): Boolean { + return true + } + + companion object { + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PwGroupV4 { + return PwGroupV4(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.kt index 2ae538656..00c2c36af 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIcon.kt @@ -23,11 +23,9 @@ import android.os.Parcelable abstract class PwIcon protected constructor() : Parcelable { - abstract val isMetaStreamIcon: Boolean - - abstract val isUnknown: Boolean - abstract val iconId: Int + abstract val isUnknown: Boolean + abstract val isMetaStreamIcon: Boolean override fun describeContents(): Int { return 0 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt index 0b949ea90..af6250b8f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconCustom.kt @@ -30,15 +30,6 @@ class PwIconCustom : PwIcon { @Transient var imageData: ByteArray = ByteArray(0) - override val isMetaStreamIcon: Boolean - get() = false - - override val isUnknown: Boolean - get() = this == ZERO - - override val iconId: Int - get() = PwIcon.UNKNOWN - constructor(uuid: UUID, data: ByteArray) : super() { this.uuid = uuid this.imageData = data @@ -82,6 +73,15 @@ class PwIconCustom : PwIcon { return uuid == other.uuid } + override val iconId: Int + get() = UNKNOWN + + override val isUnknown: Boolean + get() = this == ZERO + + override val isMetaStreamIcon: Boolean + get() = false + companion object { val ZERO = PwIconCustom(PwDatabase.UUID_ZERO, ByteArray(0)) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt index 421c73dd0..b28e09a76 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwIconStandard.kt @@ -23,13 +23,6 @@ import android.os.Parcel import android.os.Parcelable class PwIconStandard : PwIcon { - override val iconId: Int - - override val isUnknown: Boolean - get() = iconId == PwIcon.UNKNOWN - - override val isMetaStreamIcon: Boolean - get() = iconId == 0 constructor() { this.iconId = KEY @@ -69,6 +62,14 @@ class PwIconStandard : PwIcon { return iconId == other.iconId } + override val iconId: Int + + override val isUnknown: Boolean + get() = iconId == UNKNOWN + + override val isMetaStreamIcon: Boolean + get() = iconId == 0 + companion object { const val KEY = 0 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt index 1fc8436e1..9a019bab2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt @@ -31,37 +31,29 @@ abstract class PwNode, Entry : var nodeId: PwNodeId = this.initNodeId() - private var mParent: Parent? = null - private var mIcon: PwIcon = PwIconStandard() - private var mCreationDate = PwDate() - private var mLastModificationDate = PwDate() - private var mLastAccessDate = PwDate() - private var mExpireDate = PwDate.PW_NEVER_EXPIRE - - protected abstract fun initNodeId(): PwNodeId - protected abstract fun copyNodeId(nodeId: PwNodeId): PwNodeId - protected abstract fun readParentParcelable(parcel: Parcel): Parent + val id: IdType + get() = nodeId.id protected constructor() protected constructor(parcel: Parcel) { this.nodeId = parcel.readParcelable(PwNodeId::class.java.classLoader) - this.mParent = this.readParentParcelable(parcel) - this.mIcon = parcel.readParcelable(PwIconStandard::class.java.classLoader) - this.mCreationDate = parcel.readParcelable(PwDate::class.java.classLoader) - this.mLastModificationDate = parcel.readParcelable(PwDate::class.java.classLoader) - this.mLastAccessDate = parcel.readParcelable(PwDate::class.java.classLoader) - this.mExpireDate = parcel.readParcelable(PwDate::class.java.classLoader) + this.parent = this.readParentParcelable(parcel) + this.icon = parcel.readParcelable(PwIconStandard::class.java.classLoader) + this.creationTime = parcel.readParcelable(PwDate::class.java.classLoader) + this.lastModificationTime = parcel.readParcelable(PwDate::class.java.classLoader) + this.lastAccessTime = parcel.readParcelable(PwDate::class.java.classLoader) + this.expiryTime = parcel.readParcelable(PwDate::class.java.classLoader) } override fun writeToParcel(dest: Parcel, flags: Int) { dest.writeParcelable(nodeId, flags) - dest.writeParcelable(mParent, flags) - dest.writeParcelable(mIcon, flags) - dest.writeParcelable(mCreationDate, flags) - dest.writeParcelable(mLastModificationDate, flags) - dest.writeParcelable(mLastAccessDate, flags) - dest.writeParcelable(mExpireDate, flags) + dest.writeParcelable(parent, flags) + dest.writeParcelable(icon, flags) + dest.writeParcelable(creationTime, flags) + dest.writeParcelable(lastModificationTime, flags) + dest.writeParcelable(lastAccessTime, flags) + dest.writeParcelable(expiryTime, flags) } override fun describeContents(): Int { @@ -70,47 +62,36 @@ abstract class PwNode, Entry : protected fun updateWith(source: PwNode) { this.nodeId = copyNodeId(source.nodeId) - this.mParent = source.parent - this.mIcon = source.icon - this.mCreationDate = PwDate(source.mCreationDate) - this.mLastModificationDate = PwDate(source.mLastModificationDate) - this.mLastAccessDate = PwDate(source.mLastAccessDate) - this.mExpireDate = PwDate(source.mExpireDate) + this.parent = source.parent + this.icon = source.icon + this.creationTime = PwDate(source.creationTime) + this.lastModificationTime = PwDate(source.lastModificationTime) + this.lastAccessTime = PwDate(source.lastAccessTime) + this.expiryTime = PwDate(source.expiryTime) } - val id: IdType - get() = nodeId.id + protected abstract fun initNodeId(): PwNodeId + protected abstract fun copyNodeId(nodeId: PwNodeId): PwNodeId + protected abstract fun readParentParcelable(parcel: Parcel): Parent - override var parent: Parent? - get() = mParent - set(value) { mParent = value } + final override var parent: Parent? = null - override var icon: PwIcon - get() = mIcon - set(value) { mIcon = value } + final override var icon: PwIcon = PwIconStandard() - override var creationTime: PwDate - get() = mCreationDate - set(value) { mCreationDate = value } + final override var creationTime: PwDate = PwDate() - override var lastModificationTime: PwDate - get() = mLastModificationDate - set(value) { mLastModificationDate = value } + final override var lastModificationTime: PwDate = PwDate() - override var lastAccessTime: PwDate - get() = mLastAccessDate - set(value) { mLastAccessDate = value } + final override var lastAccessTime: PwDate = PwDate() - override var expiryTime: PwDate - get() = mExpireDate - set(value) { mExpireDate = value } + final override var expiryTime: PwDate = PwDate.PW_NEVER_EXPIRE - override var isExpires: Boolean + final override var isExpires: Boolean // If expireDate is before NEVER_EXPIRE date less 1 month (to be sure) - get() = mExpireDate.date.before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()) + get() = expiryTime.date.before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()) set(value) { if (!value) { - mExpireDate = PwDate.PW_NEVER_EXPIRE + expiryTime = PwDate.PW_NEVER_EXPIRE } } @@ -121,6 +102,8 @@ abstract class PwNode, Entry : return parent != null } + override fun afterAssignNewParent() {} + override fun isContainedIn(container: Parent): Boolean { var cur = this.parent while (cur != null) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt index 592519291..7ee3e1754 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNodeInterface.kt @@ -25,6 +25,8 @@ interface PwNodeInterface : NodeTimeInterface, Parcelable { fun containsParent(): Boolean + fun afterAssignNewParent() + fun isContainedIn(container: ParentGroup): Boolean fun touch(modified: Boolean, touchParents: Boolean) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 047669976..3a22633b8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -531,7 +531,7 @@ public class ImporterV4 extends Importer { } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemNotes) ) { ctxGroup.setNotes(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { - ctxGroup.setIconStandard(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); + ctxGroup.setIcon(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { ctxGroup.setIconCustom(mDatabase.getIconFactory().getIcon(ReadUuid(xpp))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemTimes) ) { @@ -590,13 +590,13 @@ public class ImporterV4 extends Importer { ctxEntry.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); mDatabase.addEntryIndex(ctxEntry); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { - ctxEntry.setIconStandard(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); + ctxEntry.setIcon(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { ctxEntry.setIconCustom(mDatabase.getIconFactory().getIcon(ReadUuid(xpp))); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemFgColor) ) { ctxEntry.setForegroundColor(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemBgColor) ) { - ctxEntry.setBackgroupColor(ReadString(xpp)); + ctxEntry.setBackgroundColor(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemOverrideUrl) ) { ctxEntry.setOverrideURL(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemTags) ) { @@ -789,7 +789,7 @@ public class ImporterV4 extends Importer { return KdbContext.CustomData; } else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - if ( ctxGroup.getNodeId() == null || ctxGroup.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { + if ( ctxGroup.getId().equals(PwDatabase.UUID_ZERO) ) { ctxGroup.setNodeId(mDatabase.newGroupId()); mDatabase.addGroupIndex(ctxGroup); } @@ -818,7 +818,7 @@ public class ImporterV4 extends Importer { return KdbContext.GroupCustomData; } else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { - if ( ctxEntry.getNodeId() == null || ctxEntry.getNodeId().getId().equals(PwDatabase.UUID_ZERO) ) { + if ( ctxEntry.getId().equals(PwDatabase.UUID_ZERO) ) { ctxEntry.setNodeId(mDatabase.newEntryId()); mDatabase.addEntryIndex(ctxEntry); } @@ -1132,6 +1132,5 @@ public class ImporterV4 extends Importer { } return null; - } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index 123c9ce89..036533337 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -314,7 +314,7 @@ public class PwDbV4Output extends PwDbOutput { writeObject(PwDatabaseV4XML.ElemUuid, group.getId()); writeObject(PwDatabaseV4XML.ElemName, group.getTitle()); writeObject(PwDatabaseV4XML.ElemNotes, group.getNotes()); - writeObject(PwDatabaseV4XML.ElemIcon, group.getIconStandard().getIconId()); + writeObject(PwDatabaseV4XML.ElemIcon, group.getIcon().getIconId()); if (!group.getIconCustom().equals(PwIconCustom.Companion.getZERO())) { writeObject(PwDatabaseV4XML.ElemCustomIconID, group.getIconCustom().getUuid()); @@ -338,15 +338,15 @@ public class PwDbV4Output extends PwDbOutput { xml.startTag(null, PwDatabaseV4XML.ElemEntry); - writeObject(PwDatabaseV4XML.ElemUuid, entry.getNodeId().getId()); - writeObject(PwDatabaseV4XML.ElemIcon, entry.getIconStandard().getIconId()); + writeObject(PwDatabaseV4XML.ElemUuid, entry.getId()); + writeObject(PwDatabaseV4XML.ElemIcon, entry.getIcon().getIconId()); if (!entry.getIconCustom().equals(PwIconCustom.Companion.getZERO())) { writeObject(PwDatabaseV4XML.ElemCustomIconID, entry.getIconCustom().getUuid()); } writeObject(PwDatabaseV4XML.ElemFgColor, entry.getForegroundColor()); - writeObject(PwDatabaseV4XML.ElemBgColor, entry.getBackgroupColor()); + writeObject(PwDatabaseV4XML.ElemBgColor, entry.getBackgroundColor()); writeObject(PwDatabaseV4XML.ElemOverrideUrl, entry.getOverrideURL()); writeObject(PwDatabaseV4XML.ElemTags, entry.getTags()); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java index 137ab94fa..37f37590e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java @@ -74,12 +74,12 @@ public class PwEntryOutputV3 { // UUID mOS.write(UUID_FIELD_TYPE); mOS.write(UUID_FIELD_SIZE); - mOS.write(Types.UUIDtoBytes(mPE.getNodeId().getId())); + mOS.write(Types.UUIDtoBytes(mPE.getId())); // Group ID mOS.write(GROUPID_FIELD_TYPE); mOS.write(LONG_FOUR); - mOS.write(LEDataOutputStream.writeIntBuf(mPE.getParent().getNodeId().getId())); + mOS.write(LEDataOutputStream.writeIntBuf(mPE.getParent().getId())); // Image ID mOS.write(IMAGEID_FIELD_TYPE); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java index a202944ed..b2142b206 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java @@ -88,7 +88,7 @@ public class EntrySearchHandlerV4 extends NodeHandler { private boolean searchID(PwEntryV4 entry) { if (mSearchParametersV4.getSearchInUUIDs()) { - String hex = UuidUtil.toHexString(entry.getNodeId().getId()); + String hex = UuidUtil.toHexString(entry.getId()); return StringUtil.INSTANCE.indexOfIgnoreCase(hex, mSearchParametersV4.getSearchString(), Locale.ENGLISH) >= 0; } From 9907af8135f69970382b2cc669e2f94af30a4eb3 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 2 Jun 2019 13:36:05 +0200 Subject: [PATCH 116/289] Kotlinized many classes --- .../keepass/tests/PwEntryTestV4.java | 10 - .../keepass/activities/EntryActivity.java | 11 +- .../keepass/activities/EntryEditActivity.java | 1 + .../keepass/activities/GroupActivity.java | 15 +- .../kunzisoft/keepass/database/AutoType.java | 89 ---- .../keepass/database/BinaryPool.java | 110 ----- .../PwDbHeader.java => CrsAlgorithm.kt} | 25 +- .../keepass/database/ExtraFields.java | 155 ------- .../database/MemoryProtectionConfig.java | 43 -- .../{NodeHandler.java => NodeHandler.kt} | 7 +- ...ameResource.java => ObjectNameResource.kt} | 8 +- .../database/PwCompressionAlgorithm.java | 47 --- ...gorithm.java => PwCompressionAlgorithm.kt} | 40 +- .../keepass/database/SortNodeEnum.java | 394 ------------------ .../keepass/database/SortNodeEnum.kt | 310 ++++++++++++++ .../keepass/database/cursor/EntryCursor.java | 57 --- .../keepass/database/cursor/EntryCursor.kt | 57 +++ .../database/cursor/EntryCursorV3.java | 23 - .../keepass/database/cursor/EntryCursorV3.kt | 25 ++ .../database/cursor/EntryCursorV4.java | 59 --- .../keepass/database/cursor/EntryCursorV4.kt | 56 +++ .../database/cursor/ExtraFieldCursor.java | 43 -- .../database/cursor/ExtraFieldCursor.kt | 38 ++ .../keepass/database/element/AutoType.kt | 87 ++++ .../keepass/database/element/BinaryPool.kt | 70 ++++ .../keepass/database/element/Database.kt | 35 +- .../database/element/EntryVersioned.kt | 1 - .../keepass/database/element/ExtraFields.kt | 152 +++++++ .../element/MemoryProtectionConfig.kt | 48 +++ .../database/element/PwDatabaseV3.java | 10 - .../element/{PwDefsV4.java => PwDbHeader.kt} | 21 +- .../database/element/PwDbHeaderV3.java | 24 +- .../database/element/PwDbHeaderV4.java | 22 +- .../keepass/database/element/PwEntryV4.kt | 2 - ...FourException.java => ArcFourException.kt} | 12 +- ...n.java => ContentFileNotFoundException.kt} | 31 +- .../exception/InvalidAlgorithmException.java | 32 -- .../exception/InvalidAlgorithmException.kt | 27 ++ ...reException.java => InvalidDBException.kt} | 18 +- ...on.java => InvalidDBSignatureException.kt} | 13 +- .../exception/InvalidDBVersionException.java | 32 -- .../exception/InvalidDBVersionException.kt | 27 ++ ...eption.java => InvalidKeyFileException.kt} | 15 +- .../exception/InvalidPasswordException.java | 36 -- .../exception/InvalidPasswordException.kt | 26 ++ .../exception/KeyFileEmptyException.kt | 26 ++ .../exception/PwDbOutputException.java | 37 -- ...BException.java => PwDbOutputException.kt} | 21 +- .../exception/SamsungClipboardException.java | 37 -- ...tion.java => SamsungClipboardException.kt} | 14 +- .../database/exception/UnknownKDF.java | 16 - .../keepass/database/exception/UnknownKDF.kt | 9 + .../iterator/EntrySearchStringIteratorV3.java | 110 ----- .../iterator/EntrySearchStringIteratorV3.kt | 95 +++++ .../load/{Importer.java => Importer.kt} | 38 +- .../keepass/database/load/ImporterV3.java | 14 +- .../keepass/database/load/ImporterV4.java | 27 +- .../database/save/PwDbHeaderOutputV3.java | 4 +- .../database/save/PwDbHeaderOutputV4.java | 12 +- .../save/PwDbInnerHeaderOutputV4.java | 2 +- .../keepass/database/save/PwDbOutput.java | 4 +- .../keepass/database/save/PwDbV3Output.java | 4 +- .../keepass/database/save/PwDbV4Output.java | 56 +-- .../database/search/EntrySearchHandlerV4.java | 115 ----- .../database/search/EntrySearchHandlerV4.kt | 103 +++++ .../keepass/database/search/SearchDbHelper.kt | 4 +- 66 files changed, 1414 insertions(+), 1698 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/AutoType.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java rename app/src/main/java/com/kunzisoft/keepass/database/{element/PwDbHeader.java => CrsAlgorithm.kt} (65%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java rename app/src/main/java/com/kunzisoft/keepass/database/{NodeHandler.java => NodeHandler.kt} (87%) rename app/src/main/java/com/kunzisoft/keepass/database/{ObjectNameResource.java => ObjectNameResource.kt} (84%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.java rename app/src/main/java/com/kunzisoft/keepass/database/{CrsAlgorithm.java => PwCompressionAlgorithm.kt} (58%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/AutoType.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/MemoryProtectionConfig.kt rename app/src/main/java/com/kunzisoft/keepass/database/element/{PwDefsV4.java => PwDbHeader.kt} (68%) rename app/src/main/java/com/kunzisoft/keepass/database/exception/{ArcFourException.java => ArcFourException.kt} (79%) rename app/src/main/java/com/kunzisoft/keepass/database/exception/{ContentFileNotFoundException.java => ContentFileNotFoundException.kt} (55%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.kt rename app/src/main/java/com/kunzisoft/keepass/database/exception/{InvalidDBSignatureException.java => InvalidDBException.kt} (74%) rename app/src/main/java/com/kunzisoft/keepass/database/exception/{InconsistentDBException.java => InvalidDBSignatureException.kt} (76%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.kt rename app/src/main/java/com/kunzisoft/keepass/database/exception/{InvalidKeyFileException.java => InvalidKeyFileException.kt} (75%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.java rename app/src/main/java/com/kunzisoft/keepass/database/exception/{InvalidDBException.java => PwDbOutputException.kt} (69%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.java rename app/src/main/java/com/kunzisoft/keepass/database/exception/{KeyFileEmptyException.java => SamsungClipboardException.kt} (75%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.kt rename app/src/main/java/com/kunzisoft/keepass/database/load/{Importer.java => Importer.kt} (50%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java index 3dc3c2718..1644ba56f 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java @@ -19,18 +19,8 @@ */ package com.kunzisoft.keepass.tests; -import com.kunzisoft.keepass.database.AutoType; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupV4; -import com.kunzisoft.keepass.database.element.PwIconCustom; -import com.kunzisoft.keepass.database.element.PwIconStandard; -import com.kunzisoft.keepass.database.security.ProtectedBinary; -import com.kunzisoft.keepass.database.security.ProtectedString; - import junit.framework.TestCase; -import java.util.UUID; - public class PwEntryTestV4 extends TestCase { public void testAssign() { /* diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 5b9070006..4dc4ad970 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -42,7 +42,7 @@ import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.ExtraFields; +import com.kunzisoft.keepass.database.element.ExtraFields; import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.element.EntryVersioned; import com.kunzisoft.keepass.database.element.PwNodeId; @@ -61,6 +61,9 @@ import com.kunzisoft.keepass.view.EntryContentsView; import java.util.ArrayList; import java.util.Date; +import kotlin.Unit; +import kotlin.jvm.functions.Function2; + import static com.kunzisoft.keepass.settings.PreferencesUtil.isClipboardNotificationsEnable; import static com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields; @@ -205,10 +208,10 @@ public class EntryActivity extends LockingHideActivity { // Add extra fields if (containsExtraFieldToCopy) { try { - mEntry.getFields().doActionToAllCustomProtectedField(new ExtraFields.ActionProtected() { + mEntry.getFields().doActionToAllCustomProtectedField(new Function2() { private int anonymousFieldNumber = 0; @Override - public void doAction(String key, ProtectedString value) { + public Unit invoke(String key, ProtectedString value) { //If value is not protected or allowed if (!value.isProtected() || PreferencesUtil.allowCopyPasswordAndProtectedFields(EntryActivity.this)) { notificationFields.add( @@ -219,6 +222,7 @@ public class EntryActivity extends LockingHideActivity { getResources())); anonymousFieldNumber++; } + return null; } }); } catch (ArrayIndexOutOfBoundsException e) { @@ -378,6 +382,7 @@ public class EntryActivity extends LockingHideActivity { getString(R.string.copy_field, label) ) ); + return null; }); } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 6d3483eeb..66f53fcd0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -523,6 +523,7 @@ public class EntryEditActivity extends LockingHideActivity entryEditCustomField.setData(key, value); entryEditCustomField.setFontVisibility(visibilityFontActivated); container.addView(entryEditCustomField); + return null; }); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index fba39248e..694634a49 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -72,6 +72,7 @@ import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable; import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable; import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; import com.kunzisoft.keepass.database.element.*; +import com.kunzisoft.keepass.database.security.ProtectedString; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; @@ -92,6 +93,9 @@ import net.cachapa.expandablelayout.ExpandableLayout; import org.jetbrains.annotations.NotNull; +import kotlin.Unit; +import kotlin.jvm.functions.Function2; + public class GroupActivity extends LockingActivity implements GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener, @@ -534,9 +538,14 @@ public class GroupActivity extends LockingActivity entryModel.setUrl(entry.getUrl()); if (entry.containsCustomFields()) { entry.getFields() - .doActionToAllCustomProtectedField( - (key, value) -> entryModel.addCustomField( - new Field(key, value.toString()))); + .doActionToAllCustomProtectedField(new Function2() { + @Override + public Unit invoke(String key, ProtectedString value) { + entryModel.addCustomField( + new Field(key, value.toString())); + return null; + } + }); } return entryModel; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java b/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java deleted file mode 100644 index 2e8fb380f..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/AutoType.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database; - -import android.os.Parcel; -import android.os.Parcelable; - -import com.kunzisoft.keepass.utils.MemUtil; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class AutoType implements Parcelable { - private static final long OBF_OPT_NONE = 0; - - public boolean enabled = true; - public long obfuscationOptions = OBF_OPT_NONE; - public String defaultSequence = ""; - private HashMap windowSeqPairs = new HashMap<>(); - - public AutoType() {} - - public AutoType(AutoType autoType) { - this.enabled = autoType.enabled; - this.obfuscationOptions = autoType.obfuscationOptions; - this.defaultSequence = autoType.defaultSequence; - for (Map.Entry entry: autoType.windowSeqPairs.entrySet()) { - this.windowSeqPairs.put(entry.getKey(), entry.getValue()); - } - } - - public AutoType(Parcel in) { - this.enabled = in.readByte() != 0; - this.obfuscationOptions = in.readLong(); - this.defaultSequence = in.readString(); - this.windowSeqPairs = MemUtil.readStringParcelableMap(in); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeByte((byte) (enabled ? 1 : 0)); - dest.writeLong(obfuscationOptions); - dest.writeString(defaultSequence); - MemUtil.writeStringParcelableMap(dest, windowSeqPairs); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public AutoType createFromParcel(Parcel in) { - return new AutoType(in); - } - - @Override - public AutoType[] newArray(int size) { - return new AutoType[size]; - } - }; - - public void put(String key, String value) { - windowSeqPairs.put(key, value); - } - - public Set> entrySet() { - return windowSeqPairs.entrySet(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java b/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java deleted file mode 100644 index 14e5f8df9..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/BinaryPool.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database; - -import com.kunzisoft.keepass.database.element.GroupVersioned; -import com.kunzisoft.keepass.database.element.PwGroupV4; -import com.kunzisoft.keepass.database.security.ProtectedBinary; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public class BinaryPool { - private HashMap pool = new HashMap<>(); - - public BinaryPool() { - } - - public BinaryPool(PwGroupV4 rootGroup) { - // TODO ? build(rootGroup); - } - - public ProtectedBinary get(int key) { - return pool.get(key); - } - - public ProtectedBinary put(int key, ProtectedBinary value) { - return pool.put(key, value); - } - - - public Set> entrySet() { - return pool.entrySet(); - } - - public void clear() { - for (Entry entry: pool.entrySet()) - entry.getValue().clear(); - pool.clear(); - } - - public Collection binaries() { - return pool.values(); - } - - private void add(Map dict) { - for (ProtectedBinary pb : dict.values()) { - add(pb); - } - - } - - public void add(ProtectedBinary pb) { - assert(pb != null); - if (findKey(pb) != -1) return; - - pool.put(findUnusedKey(), pb); - } - - public int findUnusedKey() { - int unusedKey = pool.size(); - while(get(unusedKey) != null) - unusedKey++; - return unusedKey; - } - - public int findKey(ProtectedBinary pb) { - for (Entry pair : pool.entrySet()) { - if (pair.getValue().equals(pb)) return pair.getKey(); - } - - return -1; - } - - private void build(GroupVersioned rootGroup) { - /* - rootGroup.doForEachChild(new EntryHandler() { - @Override - public boolean operate(EntryVersioned entryInterface) { - PwEntryV4 entry = entryInterface.getPwEntryV4(); - - for (PwEntryV4 histEntry : entry.getHistory()) { - add(histEntry.getBinaries()); - } - add(entry.getBinaries()); - return true; - } - }, null); - */ - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.java b/app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.kt similarity index 65% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.java rename to app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.kt index cd0c86e03..b50c1522d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.kt @@ -17,16 +17,25 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.element; +package com.kunzisoft.keepass.database -public abstract class PwDbHeader { +enum class CrsAlgorithm constructor(val id: Int) { - public static final int PWM_DBSIG_1 = 0x9AA2D903; + Null(0), + ArcFourVariant(1), + Salsa20(2), + ChaCha20(3); - /** Seed that gets hashed with the userkey to form the final key */ - public byte masterSeed[]; + companion object { + + fun fromId(num: Int): CrsAlgorithm? { + for (e in values()) { + if (e.id == num) { + return e + } + } + return null + } + } - /** IV used for content encryption */ - public byte encryptionIV[] = new byte[16]; - } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java b/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java deleted file mode 100644 index 2bb63ca5f..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/ExtraFields.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database; - -import android.os.Parcel; -import android.os.Parcelable; - -import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.utils.MemUtil; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import static com.kunzisoft.keepass.database.element.PwEntryV4.*; - -public class ExtraFields implements Parcelable { - - private Map fields; - - public ExtraFields() { - fields = new HashMap<>(); - } - - public ExtraFields(ExtraFields extraFields) { - this(); - for (Map.Entry entry: extraFields.fields.entrySet()) { - fields.put(entry.getKey(), new ProtectedString(entry.getValue())); - } - } - - public ExtraFields(Parcel in) { - fields = MemUtil.readStringParcelableMap(in, ProtectedString.class); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - MemUtil.writeStringParcelableMap(dest, flags, fields); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public ExtraFields createFromParcel(Parcel in) { - return new ExtraFields(in); - } - - @Override - public ExtraFields[] newArray(int size) { - return new ExtraFields[size]; - } - }; - - public boolean containsCustomFields() { - return !getCustomProtectedFields().keySet().isEmpty(); - } - - public boolean containsCustomFieldsProtected() { - for (Map.Entry field : getCustomProtectedFields().entrySet()) { - if (field.getValue().isProtected()) - return true; - } - return false; - } - - public boolean containsCustomFieldsNotProtected() { - for (Map.Entry field : getCustomProtectedFields().entrySet()) { - if (!field.getValue().isProtected()) - return true; - } - return false; - } - - public String getProtectedStringValue(String key) { - ProtectedString value = fields.get(key); - if ( value == null ) return ""; - return value.toString(); - } - - public void putProtectedString(String key, ProtectedString protectedString) { - fields.put(key, protectedString); - } - - public void putProtectedString(String key, String value, boolean protect) { - ProtectedString ps = new ProtectedString(protect, value); - fields.put(key, ps); - } - - /** - * @return list of standard and customized fields - */ - public Map getListOfAllFields() { - return fields; - } - - public void doActionToAllCustomProtectedField(ActionProtected actionProtected) { - for (Map.Entry field : getCustomProtectedFields().entrySet()) { - actionProtected.doAction(field.getKey(), field.getValue()); - } - } - - public interface ActionProtected { - void doAction(String key, ProtectedString value); - } - - private Map getCustomProtectedFields() { - Map protectedFields = new HashMap<>(); - if (fields.size() > 0) { - for (Map.Entry pair : fields.entrySet()) { - String key = pair.getKey(); - if (isNotStandardField(key)) { - protectedFields.put(key, pair.getValue()); - } - } - } - return protectedFields; - } - - public void removeAllCustomFields() { - Iterator> iter = fields.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry pair = iter.next(); - if (isNotStandardField(pair.getKey())) { - iter.remove(); - } - } - } - - private static boolean isNotStandardField(String key) { - return !key.equals(STR_TITLE) && !key.equals(STR_USERNAME) - && !key.equals(STR_PASSWORD) && !key.equals(STR_URL) - && !key.equals(STR_NOTES); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java b/app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java deleted file mode 100644 index 1f31f5276..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/MemoryProtectionConfig.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database; - -import com.kunzisoft.keepass.database.element.PwDefsV4; - -public class MemoryProtectionConfig { - - public boolean protectTitle = false; - public boolean protectUserName = false; - public boolean protectPassword = false; - public boolean protectUrl = false; - public boolean protectNotes = false; - - public boolean autoEnableVisualHiding = false; - - public boolean isProtected(String field) { - if (field.equalsIgnoreCase(PwDefsV4.TITLE_FIELD)) return protectTitle; - if (field.equalsIgnoreCase(PwDefsV4.USERNAME_FIELD)) return protectUserName; - if (field.equalsIgnoreCase(PwDefsV4.PASSWORD_FIELD)) return protectPassword; - if (field.equalsIgnoreCase(PwDefsV4.URL_FIELD)) return protectUrl; - if (field.equalsIgnoreCase(PwDefsV4.NOTES_FIELD)) return protectNotes; - - return false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.java b/app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.kt similarity index 87% rename from app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.java rename to app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.kt index 55aed8d20..bf0a46f2c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/NodeHandler.kt @@ -17,14 +17,13 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database /** "Delegate" class for operating on each group when traversing all of * them * @author bpellin - * */ -public abstract class NodeHandler { - public abstract boolean operate(T group); +abstract class NodeHandler { + abstract fun operate(node: T): Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/ObjectNameResource.java b/app/src/main/java/com/kunzisoft/keepass/database/ObjectNameResource.kt similarity index 84% rename from app/src/main/java/com/kunzisoft/keepass/database/ObjectNameResource.java rename to app/src/main/java/com/kunzisoft/keepass/database/ObjectNameResource.kt index 745d605e2..c2c0cb9f7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/ObjectNameResource.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/ObjectNameResource.kt @@ -17,13 +17,13 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database -import android.content.res.Resources; +import android.content.res.Resources /** * Interface to generify items with a name resource, that can be (for example) visible in a list */ -public interface ObjectNameResource { - String getName(Resources resources); +interface ObjectNameResource { + fun getName(resources: Resources): String } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.java b/app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.java deleted file mode 100644 index d12f15cc9..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database; - -public enum PwCompressionAlgorithm { - - None(0), - Gzip(1); - - // Note: We can get away with using int's to store unsigned 32-bit ints - // since we won't do arithmetic on these values (also unlikely to - // reach negative ids). - public final int id; - public static final int count = 2; - - private PwCompressionAlgorithm(int num) { - id = num; - } - - public static PwCompressionAlgorithm fromId(int num) { - for ( PwCompressionAlgorithm e : PwCompressionAlgorithm.values() ) { - if ( e.id == num ) { - return e; - } - } - - return null; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.java b/app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.kt similarity index 58% rename from app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.java rename to app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.kt index 81da5be75..a292d5e90 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.kt @@ -17,30 +17,26 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database; +package com.kunzisoft.keepass.database -public enum CrsAlgorithm { - - Null(0), - ArcFourVariant(1), - Salsa20(2), - ChaCha20(3); +// Note: We can get away with using int's to store unsigned 32-bit ints +// since we won't do arithmetic on these values (also unlikely to +// reach negative ids). +enum class PwCompressionAlgorithm constructor(val id: Int) { - public static final int count = 4; - public final int id; - - private CrsAlgorithm(int num) { - id = num; - } + None(0), + Gzip(1); - public static CrsAlgorithm fromId(int num) { - for ( CrsAlgorithm e : CrsAlgorithm.values() ) { - if ( e.id == num ) { - return e; - } - } - - return null; - } + companion object { + + fun fromId(num: Int): PwCompressionAlgorithm? { + for (e in values()) { + if (e.id == num) { + return e + } + } + return null + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java deleted file mode 100644 index d9d616bf0..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.java +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ - -package com.kunzisoft.keepass.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; - -public enum SortNodeEnum { - DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME; - - public Comparator getNodeComparator(boolean ascending, boolean groupsBefore) { - switch (this) { - case DB: - return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort - default: - case TITLE: - return new NodeTitleComparator(ascending, groupsBefore); - case USERNAME: - return new NodeCreationComparator(ascending, groupsBefore); // TODO Sort - case CREATION_TIME: - return new NodeCreationComparator(ascending, groupsBefore); - case LAST_MODIFY_TIME: - return new NodeLastModificationComparator(ascending, groupsBefore); - case LAST_ACCESS_TIME: - return new NodeLastAccessComparator(ascending, groupsBefore); - } - } - - private static abstract class NodeComparator implements Comparator { - boolean ascending; - boolean groupsBefore; - - NodeComparator(boolean ascending, boolean groupsBefore) { - this.ascending = ascending; - this.groupsBefore = groupsBefore; - } - - int compareWith(Comparator comparatorGroup, - Comparator comparatorEntry, - NodeVersioned object1, - NodeVersioned object2, - int resultOfNodeMethodCompare) { - if (object1.equals(object2)) - return 0; - - if (object1 instanceof GroupVersioned) { - if (object2 instanceof GroupVersioned) { - return comparatorGroup - .compare((GroupVersioned) object1, (GroupVersioned) object2); - } else if (object2 instanceof EntryVersioned) { - if(groupsBefore) - return -1; - else - return 1; - } else { - return -1; - } - } else if (object1 instanceof EntryVersioned) { - if(object2 instanceof EntryVersioned) { - return comparatorEntry - .compare((EntryVersioned) object1, (EntryVersioned) object2); - } else if (object2 instanceof GroupVersioned) { - if(groupsBefore) - return 1; - else - return -1; - } else { - return -1; - } - } - - // If same name, can be different - if (resultOfNodeMethodCompare == 0) - return object1.hashCode() - object2.hashCode(); - return resultOfNodeMethodCompare; - } - } - - /** - * Comparator of Node by Title, Groups first, Entries second - */ - public static class NodeTitleComparator extends NodeComparator { - - NodeTitleComparator(boolean ascending, boolean groupsBefore) { - super(ascending, groupsBefore); - } - - public int compare(NodeVersioned object1, NodeVersioned object2) { - - return compareWith( - new GroupNameComparator(ascending), - new EntryNameComparator(ascending), - object1, - object2, - object1.getTitle() - .compareToIgnoreCase(object2.getTitle())); - } - } - - /** - * Comparator of node by creation, Groups first, Entries second - */ - public static class NodeCreationComparator extends NodeComparator { - - - NodeCreationComparator(boolean ascending, boolean groupsBefore) { - super(ascending, groupsBefore); - } - - @Override - public int compare(NodeVersioned object1, NodeVersioned object2) { - - return compareWith( - new GroupCreationComparator(ascending), - new EntryCreationComparator(ascending), - object1, - object2, - object1.getCreationTime().getDate() - .compareTo(object2.getCreationTime().getDate())); - } - } - - /** - * Comparator of node by last modification, Groups first, Entries second - */ - public static class NodeLastModificationComparator extends NodeComparator { - - NodeLastModificationComparator(boolean ascending, boolean groupsBefore) { - super(ascending, groupsBefore); - } - - @Override - public int compare(NodeVersioned object1, NodeVersioned object2) { - - return compareWith( - new GroupLastModificationComparator(ascending), - new EntryLastModificationComparator(ascending), - object1, - object2, - object1.getLastModificationTime().getDate() - .compareTo(object2.getLastModificationTime().getDate())); - } - } - - /** - * Comparator of node by last access, Groups first, Entries second - */ - public static class NodeLastAccessComparator extends NodeComparator { - - NodeLastAccessComparator(boolean ascending, boolean groupsBefore) { - super(ascending, groupsBefore); - } - - @Override - public int compare(NodeVersioned object1, NodeVersioned object2) { - - return compareWith( - new GroupLastAccessComparator(ascending), - new EntryLastAccessComparator(ascending), - object1, - object2, - object1.getLastAccessTime().getDate() - .compareTo(object2.getLastAccessTime().getDate())); - } - } - - private static abstract class AscendingComparator implements Comparator { - - private boolean ascending; - - AscendingComparator(boolean ascending) { - this.ascending = ascending; - } - - int compareWithAscending(int basicCompareResult) { - // If descending, revert - if (!ascending) - return -basicCompareResult; - - return basicCompareResult; - } - } - - /** - * Group comparator by name - */ - public static class GroupNameComparator extends AscendingComparator { - - GroupNameComparator(boolean ascending) { - super(ascending); - } - - public int compare(GroupVersioned object1, GroupVersioned object2) { - if (object1.equals(object2)) - return 0; - - int groupNameComp = object1.getTitle().compareToIgnoreCase(object2.getTitle()); - // If same name, can be different - if (groupNameComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(groupNameComp); - } - } - - /** - * Group comparator by name - */ - public static class GroupCreationComparator extends AscendingComparator { - - GroupCreationComparator(boolean ascending) { - super(ascending); - } - - public int compare(GroupVersioned object1, GroupVersioned object2) { - if (object1.equals(object2)) - return 0; - - int groupCreationComp = object1.getCreationTime().getDate() - .compareTo(object2.getCreationTime().getDate()); - // If same creation, can be different - if (groupCreationComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(groupCreationComp); - } - } - - /** - * Group comparator by last modification - */ - public static class GroupLastModificationComparator extends AscendingComparator { - - GroupLastModificationComparator(boolean ascending) { - super(ascending); - } - - public int compare(GroupVersioned object1, GroupVersioned object2) { - if (object1.equals(object2)) - return 0; - - int groupLastModificationComp = object1.getLastModificationTime().getDate() - .compareTo(object2.getLastModificationTime().getDate()); - // If same creation, can be different - if (groupLastModificationComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(groupLastModificationComp); - } - } - - /** - * Group comparator by last access - */ - public static class GroupLastAccessComparator extends AscendingComparator { - - GroupLastAccessComparator(boolean ascending) { - super(ascending); - } - - public int compare(GroupVersioned object1, GroupVersioned object2) { - if (object1.equals(object2)) - return 0; - - int groupLastAccessComp = object1.getLastAccessTime().getDate() - .compareTo(object2.getLastAccessTime().getDate()); - // If same creation, can be different - if (groupLastAccessComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(groupLastAccessComp); - } - } - - /** - * Comparator of Entry by Name - */ - public static class EntryNameComparator extends AscendingComparator { - - EntryNameComparator(boolean ascending) { - super(ascending); - } - - public int compare(EntryVersioned object1, EntryVersioned object2) { - if (object1.equals(object2)) - return 0; - - int entryTitleComp = object1.getTitle().compareToIgnoreCase(object2.getTitle()); - // If same title, can be different - if (entryTitleComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(entryTitleComp); - } - } - - /** - * Comparator of Entry by Creation - */ - public static class EntryCreationComparator extends AscendingComparator { - - EntryCreationComparator(boolean ascending) { - super(ascending); - } - - public int compare(EntryVersioned object1, EntryVersioned object2) { - if (object1.equals(object2)) - return 0; - - int entryCreationComp = object1.getCreationTime().getDate() - .compareTo(object2.getCreationTime().getDate()); - // If same creation, can be different - if (entryCreationComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(entryCreationComp); - } - } - - /** - * Comparator of Entry by Last Modification - */ - public static class EntryLastModificationComparator extends AscendingComparator { - - EntryLastModificationComparator(boolean ascending) { - super(ascending); - } - - public int compare(EntryVersioned object1, EntryVersioned object2) { - if (object1.equals(object2)) - return 0; - - int entryLastModificationComp = object1.getLastModificationTime().getDate() - .compareTo(object2.getLastModificationTime().getDate()); - // If same creation, can be different - if (entryLastModificationComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(entryLastModificationComp); - } - } - - /** - * Comparator of Entry by Last Access - */ - public static class EntryLastAccessComparator extends AscendingComparator { - - EntryLastAccessComparator(boolean ascending) { - super(ascending); - } - - public int compare(EntryVersioned object1, EntryVersioned object2) { - if (object1.equals(object2)) - return 0; - - int entryLastAccessComp = object1.getLastAccessTime().getDate() - .compareTo(object2.getLastAccessTime().getDate()); - // If same creation, can be different - if (entryLastAccessComp == 0) { - return object1.hashCode() - object2.hashCode(); - } - - return compareWithAscending(entryLastAccessComp); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt new file mode 100644 index 000000000..5afd0ea7e --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt @@ -0,0 +1,310 @@ +/* + * Copyright 2018 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ + +package com.kunzisoft.keepass.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 + +enum class SortNodeEnum { + DB, TITLE, USERNAME, CREATION_TIME, LAST_MODIFY_TIME, LAST_ACCESS_TIME; + + fun getNodeComparator(ascending: Boolean, groupsBefore: Boolean): Comparator { + return when (this) { + DB -> NodeCreationComparator(ascending, groupsBefore) // TODO Sort + TITLE -> NodeTitleComparator(ascending, groupsBefore) + USERNAME -> NodeCreationComparator(ascending, groupsBefore) // TODO Sort + CREATION_TIME -> NodeCreationComparator(ascending, groupsBefore) + LAST_MODIFY_TIME -> NodeLastModificationComparator(ascending, groupsBefore) + LAST_ACCESS_TIME -> NodeLastAccessComparator(ascending, groupsBefore) + } + } + + abstract class NodeComparator internal constructor(internal var ascending: Boolean, private var groupsBefore: Boolean) : Comparator { + + internal fun compareWith(comparatorGroup: Comparator, + comparatorEntry: Comparator, + object1: NodeVersioned, + object2: NodeVersioned, + resultOfNodeMethodCompare: Int): 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 (groupsBefore) + -1 + else + 1 + } else { + -1 + } + } else if (object1 is EntryVersioned) { + return if (object2 is EntryVersioned) { + comparatorEntry + .compare(object1, object2) + } else if (object2 is GroupVersioned) { + if (groupsBefore) + 1 + else + -1 + } else { + -1 + } + } + + // If same name, can be different + return if (resultOfNodeMethodCompare == 0) object1.hashCode() - object2.hashCode() else resultOfNodeMethodCompare + } + } + + /** + * Comparator of Node by Title, Groups first, Entries second + */ + class NodeTitleComparator internal constructor(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)) + } + } + + /** + * Comparator of node by creation, Groups first, Entries second + */ + class NodeCreationComparator internal constructor(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)) + } + } + + /** + * Comparator of node by last modification, Groups first, Entries second + */ + class NodeLastModificationComparator internal constructor(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)) + } + } + + /** + * Comparator of node by last access, Groups first, Entries second + */ + class NodeLastAccessComparator internal constructor(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)) + } + } + + abstract class AscendingComparator internal constructor(private val ascending: Boolean) : Comparator { + + internal fun compareWithAscending(basicCompareResult: Int): Int { + // If descending, revert + return if (!ascending) -basicCompareResult else basicCompareResult + + } + } + + /** + * Group comparator by name + */ + class GroupNameComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { + + override fun compare(object1: GroupVersioned, object2: GroupVersioned): 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) { + object1.hashCode() - object2.hashCode() + } else compareWithAscending(groupNameComp) + + } + } + + /** + * Group comparator by name + */ + class GroupCreationComparator internal constructor(ascending: Boolean) : AscendingComparator(ascending) { + + override fun compare(object1: GroupVersioned, object2: GroupVersioned): Int { + if (object1 == object2) + return 0 + + val groupCreationComp = object1.creationTime.date + .compareTo(object2.creationTime.date) + // 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 + .compareTo(object2.lastModificationTime.date) + // 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 + .compareTo(object2.lastAccessTime.date) + // 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) + + } + } + + /** + * 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 + .compareTo(object2.creationTime.date) + // 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 + .compareTo(object2.lastModificationTime.date) + // 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 + .compareTo(object2.lastAccessTime.date) + // 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/cursor/EntryCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java deleted file mode 100644 index ddc70d1d4..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.kunzisoft.keepass.database.cursor; - -import android.database.MatrixCursor; -import android.provider.BaseColumns; - -import com.kunzisoft.keepass.database.element.*; - -import java.util.UUID; - -public abstract class EntryCursor extends MatrixCursor { - - protected long entryId; - public static final String _ID = BaseColumns._ID; - public static final String COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS = "UUID_most_significant_bits"; - public static final String COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS = "UUID_least_significant_bits"; - public static final String COLUMN_INDEX_TITLE = "title"; - public static final String COLUMN_INDEX_ICON_STANDARD = "icon_standard"; - public static final String COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS = "icon_custom_UUID_most_significant_bits"; - public static final String COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS = "icon_custom_UUID_least_significant_bits"; - public static final String COLUMN_INDEX_USERNAME = "username"; - public static final String COLUMN_INDEX_PASSWORD = "password"; - public static final String COLUMN_INDEX_URL = "URL"; - public static final String COLUMN_INDEX_NOTES = "notes"; - - public EntryCursor() { - super(new String[]{ _ID, - COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS, - COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS, - COLUMN_INDEX_TITLE, - COLUMN_INDEX_ICON_STANDARD, - COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS, - COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS, - COLUMN_INDEX_USERNAME, - COLUMN_INDEX_PASSWORD, - COLUMN_INDEX_URL, - COLUMN_INDEX_NOTES}); - entryId = 0; - } - - public abstract void addEntry(PwEntryV entry); - - public void populateEntry(PwEntryV pwEntry, PwIconFactory iconFactory) { - pwEntry.setNodeId(new PwNodeIdUUID( - new UUID(getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), - getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))))); - pwEntry.setTitle(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE))); - - PwIconStandard iconStandard = iconFactory.getIcon(getInt(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_STANDARD))); - pwEntry.setIcon(iconStandard); - - pwEntry.setUsername(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_USERNAME))); - pwEntry.setPassword(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_PASSWORD))); - pwEntry.setUrl(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_URL))); - pwEntry.setNotes(getString(getColumnIndex(EntryCursor.COLUMN_INDEX_NOTES))); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt new file mode 100644 index 000000000..67f89fd51 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursor.kt @@ -0,0 +1,57 @@ +package com.kunzisoft.keepass.database.cursor + +import android.database.MatrixCursor +import android.provider.BaseColumns + +import com.kunzisoft.keepass.database.element.* + +import java.util.UUID + +abstract class EntryCursor> : MatrixCursor(arrayOf( + _ID, + COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS, + COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS, + COLUMN_INDEX_TITLE, + COLUMN_INDEX_ICON_STANDARD, + COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS, + COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS, + COLUMN_INDEX_USERNAME, + COLUMN_INDEX_PASSWORD, + COLUMN_INDEX_URL, + COLUMN_INDEX_NOTES + )) { + + protected var entryId: Long = 0 + + abstract fun addEntry(entry: PwEntryV) + + open fun populateEntry(pwEntry: PwEntryV, iconFactory: PwIconFactory) { + pwEntry.nodeId = PwNodeIdUUID( + UUID(getLong(getColumnIndex(COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), + getLong(getColumnIndex(COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS)))) + pwEntry.title = getString(getColumnIndex(COLUMN_INDEX_TITLE)) + + val iconStandard = iconFactory.getIcon(getInt(getColumnIndex(COLUMN_INDEX_ICON_STANDARD))) + pwEntry.icon = iconStandard + + pwEntry.username = getString(getColumnIndex(COLUMN_INDEX_USERNAME)) + pwEntry.password = getString(getColumnIndex(COLUMN_INDEX_PASSWORD)) + pwEntry.url = getString(getColumnIndex(COLUMN_INDEX_URL)) + pwEntry.notes = getString(getColumnIndex(COLUMN_INDEX_NOTES)) + } + + companion object { + const val _ID = BaseColumns._ID + const val COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS = "UUID_most_significant_bits" + const val COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS = "UUID_least_significant_bits" + const val COLUMN_INDEX_TITLE = "title" + const val COLUMN_INDEX_ICON_STANDARD = "icon_standard" + const val COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS = "icon_custom_UUID_most_significant_bits" + const val COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS = "icon_custom_UUID_least_significant_bits" + const val COLUMN_INDEX_USERNAME = "username" + const val COLUMN_INDEX_PASSWORD = "password" + const val COLUMN_INDEX_URL = "URL" + const val COLUMN_INDEX_NOTES = "notes" + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java deleted file mode 100644 index 839ed1c0a..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.kunzisoft.keepass.database.cursor; - -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.element.PwEntryV3; - -public class EntryCursorV3 extends EntryCursor { - - public void addEntry(PwEntryV3 entry) { - addRow(new Object[] {entryId, - entry.getId().getMostSignificantBits(), - entry.getId().getLeastSignificantBits(), - entry.getTitle(), - entry.getIcon().getIconId(), - PwDatabase.UUID_ZERO.getMostSignificantBits(), - PwDatabase.UUID_ZERO.getLeastSignificantBits(), - entry.getUsername(), - entry.getPassword(), - entry.getUrl(), - entry.getNotes()}); - entryId++; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.kt new file mode 100644 index 000000000..9ef52fbe5 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV3.kt @@ -0,0 +1,25 @@ +package com.kunzisoft.keepass.database.cursor + +import com.kunzisoft.keepass.database.element.PwDatabase +import com.kunzisoft.keepass.database.element.PwEntryV3 + +class EntryCursorV3 : EntryCursor() { + + override fun addEntry(entry: PwEntryV3) { + addRow(arrayOf( + entryId, + entry.id.mostSignificantBits, + entry.id.leastSignificantBits, + entry.title, + entry.icon.iconId, + PwDatabase.UUID_ZERO.mostSignificantBits, + PwDatabase.UUID_ZERO.leastSignificantBits, + entry.username, + entry.password, + entry.url, + entry.notes + )) + entryId++ + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java deleted file mode 100644 index e2eb4033c..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.kunzisoft.keepass.database.cursor; - -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwIconCustom; -import com.kunzisoft.keepass.database.element.PwIconFactory; - -import java.util.UUID; - -public class EntryCursorV4 extends EntryCursor { - - private ExtraFieldCursor extraFieldCursor; - - public EntryCursorV4() { - super(); - extraFieldCursor = new ExtraFieldCursor(); - } - - public void addEntry(PwEntryV4 entry) { - addRow(new Object[] {entryId, - entry.getId().getMostSignificantBits(), - entry.getId().getLeastSignificantBits(), - entry.getTitle(), - entry.getIcon().getIconId(), - entry.getIconCustom().getUuid().getMostSignificantBits(), - entry.getIconCustom().getUuid().getLeastSignificantBits(), - entry.getUsername(), - entry.getPassword(), - entry.getUrl(), - entry.getNotes()}); - - entry.getFields().doActionToAllCustomProtectedField((key, value) -> { - extraFieldCursor.addExtraField(entryId, key, value); - }); - - entryId++; - } - - public void populateEntry(PwEntryV4 pwEntry, PwIconFactory iconFactory) { - super.populateEntry(pwEntry, iconFactory); - - // Retrieve custom icon - PwIconCustom iconCustom = iconFactory.getIcon( - new UUID(getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), - getLong(getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))); - pwEntry.setIconCustom(iconCustom); - - // Retrieve extra fields - if (extraFieldCursor.moveToFirst()) { - while (!extraFieldCursor.isAfterLast()) { - // Add a new extra field only if entryId is the one we want - if (extraFieldCursor.getLong(extraFieldCursor.getColumnIndex(ExtraFieldCursor.FOREIGN_KEY_ENTRY_ID)) - == getLong(getColumnIndex(EntryCursor._ID))) { - extraFieldCursor.populateExtraFieldInEntry(pwEntry); - } - extraFieldCursor.moveToNext(); - } - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.kt new file mode 100644 index 000000000..370cc207e --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/EntryCursorV4.kt @@ -0,0 +1,56 @@ +package com.kunzisoft.keepass.database.cursor + +import com.kunzisoft.keepass.database.element.PwEntryV4 +import com.kunzisoft.keepass.database.element.PwIconFactory + +import java.util.UUID + +class EntryCursorV4 : EntryCursor() { + + private val extraFieldCursor: ExtraFieldCursor = ExtraFieldCursor() + + override fun addEntry(entry: PwEntryV4) { + addRow(arrayOf( + entryId, + entry.id.mostSignificantBits, + entry.id.leastSignificantBits, + entry.title, + entry.icon.iconId, + entry.iconCustom.uuid.mostSignificantBits, + entry.iconCustom.uuid.leastSignificantBits, + entry.username, + entry.password, + entry.url, + entry.notes + )) + + entry.fields.doActionToAllCustomProtectedField { key, value -> + extraFieldCursor.addExtraField(entryId, key, value) + } + + entryId++ + } + + override fun populateEntry(pwEntry: PwEntryV4, iconFactory: PwIconFactory) { + super.populateEntry(pwEntry, iconFactory) + + // Retrieve custom icon + val iconCustom = iconFactory.getIcon( + UUID(getLong(getColumnIndex(COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), + getLong(getColumnIndex(COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))) + pwEntry.iconCustom = iconCustom + + // Retrieve extra fields + if (extraFieldCursor.moveToFirst()) { + while (!extraFieldCursor.isAfterLast) { + // Add a new extra field only if entryId is the one we want + if (extraFieldCursor.getLong(extraFieldCursor + .getColumnIndex(ExtraFieldCursor.FOREIGN_KEY_ENTRY_ID)) + == getLong(getColumnIndex(_ID))) { + extraFieldCursor.populateExtraFieldInEntry(pwEntry) + } + extraFieldCursor.moveToNext() + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java b/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java deleted file mode 100644 index c5159968a..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.kunzisoft.keepass.database.cursor; - -import android.database.MatrixCursor; -import android.provider.BaseColumns; - -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.security.ProtectedString; - -public class ExtraFieldCursor extends MatrixCursor { - - private long fieldId; - public static final String _ID = BaseColumns._ID; - public static final String FOREIGN_KEY_ENTRY_ID = "entry_id"; - public static final String COLUMN_LABEL = "label"; - public static final String COLUMN_PROTECTION = "protection"; - public static final String COLUMN_VALUE = "value"; - - public ExtraFieldCursor() { - super(new String[]{ _ID, - FOREIGN_KEY_ENTRY_ID, - COLUMN_LABEL, - COLUMN_PROTECTION, - COLUMN_VALUE}); - fieldId = 0; - } - - public synchronized void addExtraField(long entryId, String label, ProtectedString value) { - addRow(new Object[] {fieldId, - entryId, - label, - (value.isProtected()) ? 1 : 0, - value.toString()}); - fieldId++; - } - - public void populateExtraFieldInEntry(PwEntryV4 pwEntry) { - - pwEntry.addExtraField(getString(getColumnIndex(ExtraFieldCursor.COLUMN_LABEL)), - new ProtectedString((getInt(getColumnIndex(ExtraFieldCursor.COLUMN_PROTECTION)) > 0), - getString(getColumnIndex(ExtraFieldCursor.COLUMN_VALUE)))); - - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt b/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt new file mode 100644 index 000000000..a79321a61 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt @@ -0,0 +1,38 @@ +package com.kunzisoft.keepass.database.cursor + +import android.database.MatrixCursor +import android.provider.BaseColumns + +import com.kunzisoft.keepass.database.element.PwEntryV4 +import com.kunzisoft.keepass.database.security.ProtectedString + +class ExtraFieldCursor : MatrixCursor(arrayOf( + _ID, + FOREIGN_KEY_ENTRY_ID, + COLUMN_LABEL, + COLUMN_PROTECTION, + COLUMN_VALUE + )) { + + private var fieldId: Long = 0 + + @Synchronized + fun addExtraField(entryId: Long, label: String, value: ProtectedString) { + addRow(arrayOf(fieldId, entryId, label, if (value.isProtected) 1 else 0, value.toString())) + fieldId++ + } + + fun populateExtraFieldInEntry(pwEntry: PwEntryV4) { + pwEntry.addExtraField(getString(getColumnIndex(COLUMN_LABEL)), + ProtectedString(getInt(getColumnIndex(COLUMN_PROTECTION)) > 0, + getString(getColumnIndex(COLUMN_VALUE)))) + } + + companion object { + const val _ID = BaseColumns._ID + const val FOREIGN_KEY_ENTRY_ID = "entry_id" + const val COLUMN_LABEL = "label" + const val COLUMN_PROTECTION = "protection" + const val COLUMN_VALUE = "value" + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/AutoType.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/AutoType.kt new file mode 100644 index 000000000..3479f759f --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/AutoType.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2018 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import com.kunzisoft.keepass.utils.MemUtil + +import java.util.HashMap + +class AutoType : Parcelable { + + var enabled = true + var obfuscationOptions = OBF_OPT_NONE + var defaultSequence = "" + private var windowSeqPairs = HashMap() + + constructor() + + constructor(autoType: AutoType) { + this.enabled = autoType.enabled + this.obfuscationOptions = autoType.obfuscationOptions + this.defaultSequence = autoType.defaultSequence + for ((key, value) in autoType.windowSeqPairs) { + this.windowSeqPairs[key] = value + } + } + + constructor(parcel: Parcel) { + this.enabled = parcel.readByte().toInt() != 0 + this.obfuscationOptions = parcel.readLong() + this.defaultSequence = parcel.readString() + this.windowSeqPairs = MemUtil.readStringParcelableMap(parcel) + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeByte((if (enabled) 1 else 0).toByte()) + dest.writeLong(obfuscationOptions) + dest.writeString(defaultSequence) + MemUtil.writeStringParcelableMap(dest, windowSeqPairs) + } + + fun put(key: String, value: String) { + windowSeqPairs[key] = value + } + + fun entrySet(): Set> { + return windowSeqPairs.entries + } + + companion object { + private const val OBF_OPT_NONE: Long = 0 + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): AutoType { + return AutoType(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt new file mode 100644 index 000000000..5a90d01e8 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import com.kunzisoft.keepass.database.security.ProtectedBinary +import java.util.HashMap +import kotlin.collections.Map.Entry + +class BinaryPool { + // TODO SparseArray + private val pool = HashMap() + + operator fun get(key: Int): ProtectedBinary? { + return pool[key] + } + + fun put(key: Int, value: ProtectedBinary) { + pool[key] = value + } + + fun entrySet(): Set> { + return pool.entries + } + + fun clear() { + for ((_, value) in pool) + value.clear() + pool.clear() + } + + fun binaries(): Collection { + return pool.values + } + + fun add(protectedBinary: ProtectedBinary) { + if (findKey(protectedBinary) != -1) return + pool[findUnusedKey()] = protectedBinary + } + + fun findUnusedKey(): Int { + var unusedKey = pool.size + while (get(unusedKey) != null) + unusedKey++ + return unusedKey + } + + fun findKey(pb: ProtectedBinary): Int { + for ((key, value) in pool) { + if (value == pb) return key + } + return -1 + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 0bd1b7742..86e1561c1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -331,8 +331,12 @@ class Database { if (searchResult != null) { for (entry in searchResult.getChildEntries()) { if (!entry.isMetaStream) { // TODO metastream - cursorV3?.addEntry(entry.pwEntryV3) - cursorV4?.addEntry(entry.pwEntryV4) + entry.pwEntryV3?.let { + cursorV3?.addEntry(it) + } + entry.pwEntryV4?.let { + cursorV4?.addEntry(it) + } } } } @@ -341,14 +345,22 @@ class Database { } fun getEntryFrom(cursor: Cursor): EntryVersioned? { - val iconFactory = pwDatabaseV3?.getIconFactory() ?: pwDatabaseV4?.getIconFactory() + val iconFactory = pwDatabaseV3?.getIconFactory() ?: pwDatabaseV4?.getIconFactory() ?: PwIconFactory() val entry = createEntry() // TODO invert field reference manager entry?.let { entryVersioned -> startManageEntry(entryVersioned) - pwDatabaseV3?.let { (cursor as EntryCursorV3).populateEntry(entryVersioned.pwEntryV3, iconFactory) } - pwDatabaseV4?.let { (cursor as EntryCursorV4).populateEntry(entryVersioned.pwEntryV4, iconFactory) } + pwDatabaseV3?.let { + entryVersioned.pwEntryV3?.let { entryV3 -> + (cursor as EntryCursorV3).populateEntry(entryV3, iconFactory) + } + } + pwDatabaseV4?.let { + entryVersioned.pwEntryV4?.let { entryV4 -> + (cursor as EntryCursorV4).populateEntry(entryV4, iconFactory) + } + } stopManageEntry(entryVersioned) } @@ -416,8 +428,7 @@ class Database { fun closeAndClear(context: Context) { drawFactory.clearCache() // Delete the cache of the database if present - if (pwDatabaseV4 != null) - pwDatabaseV4!!.clearCache() + pwDatabaseV4?.clearCache() // In all cases, delete all the files in the temp dir try { FileUtils.cleanDirectory(context.filesDir) @@ -617,15 +628,15 @@ class Database { fun deleteGroup(group: GroupVersioned) { group.doForEachChildAndForIt( object : NodeHandler() { - override fun operate(entry: EntryVersioned): Boolean { - deleteEntry(entry) + override fun operate(node: EntryVersioned): Boolean { + deleteEntry(node) return true } }, object : NodeHandler() { - override fun operate(group: GroupVersioned): Boolean { - group.parent?.let { - removeGroupFrom(group, it) + override fun operate(node: GroupVersioned): Boolean { + node.parent?.let { + removeGroupFrom(node, it) } return true } 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 1c6fef28c..affea85f5 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 @@ -2,7 +2,6 @@ package com.kunzisoft.keepass.database.element import android.os.Parcel import android.os.Parcelable -import com.kunzisoft.keepass.database.ExtraFields import com.kunzisoft.keepass.database.security.ProtectedString import java.util.* import kotlin.collections.ArrayList diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt new file mode 100644 index 000000000..e833d6c6d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt @@ -0,0 +1,152 @@ +/* + * Copyright 2018 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.utils.MemUtil + +import java.util.HashMap + +import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_TITLE +import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_USERNAME +import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_PASSWORD +import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_URL +import com.kunzisoft.keepass.database.element.PwEntryV4.Companion.STR_NOTES + +class ExtraFields : Parcelable { + + private var fields: MutableMap = HashMap() + + /** + * @return list of standard and customized fields + */ + val listOfAllFields: Map + get() = fields + + private val customProtectedFields: Map + get() { + val protectedFields = HashMap() + if (fields.isNotEmpty()) { + for ((key, value) in fields) { + if (isNotStandardField(key)) { + protectedFields[key] = value + } + } + } + return protectedFields + } + + constructor() + + constructor(extraFields: ExtraFields) : this() { + for ((key, value) in extraFields.fields) { + fields[key] = ProtectedString(value) + } + } + + constructor(parcel: Parcel) { + fields = MemUtil.readStringParcelableMap(parcel, ProtectedString::class.java) + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + MemUtil.writeStringParcelableMap(dest, flags, fields) + } + + fun containsCustomFields(): Boolean { + return customProtectedFields.keys.isNotEmpty() + } + + fun containsCustomFieldsProtected(): Boolean { + for ((_, value) in customProtectedFields) { + if (value.isProtected) + return true + } + return false + } + + fun containsCustomFieldsNotProtected(): Boolean { + for ((_, value) in customProtectedFields) { + if (!value.isProtected) + return true + } + return false + } + + fun getProtectedStringValue(key: String): String { + val value = fields[key] ?: return "" + return value.toString() + } + + fun putProtectedString(key: String, protectedString: ProtectedString) { + fields[key] = protectedString + } + + fun putProtectedString(key: String, value: String, protect: Boolean) { + val ps = ProtectedString(protect, value) + fields[key] = ps + } + + fun doActionToAllCustomProtectedField(actionProtected: (key: String, value: ProtectedString)-> Unit) { + for ((key, value) in customProtectedFields) { + actionProtected.invoke(key, value) + } + } + + interface ActionProtected { + fun doAction(key: String, value: ProtectedString) + } + + fun removeAllCustomFields() { + val iterator = fields.entries.iterator() + while (iterator.hasNext()) { + val pair = iterator.next() + if (isNotStandardField(pair.key)) { + iterator.remove() + } + } + } + + companion object { + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): ExtraFields { + return ExtraFields(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + private fun isNotStandardField(key: String): Boolean { + return (key != STR_TITLE && key != STR_USERNAME + && key != STR_PASSWORD && key != STR_URL + && key != STR_NOTES) + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/MemoryProtectionConfig.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/MemoryProtectionConfig.kt new file mode 100644 index 000000000..1e4dbda13 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/MemoryProtectionConfig.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +class MemoryProtectionConfig { + + var protectTitle = false + var protectUserName = false + var protectPassword = false + var protectUrl = false + var protectNotes = false + + var autoEnableVisualHiding = false + + fun isProtected(field: String): Boolean { + if (field.equals(ProtectDefinition.TITLE_FIELD, ignoreCase = true)) return protectTitle + if (field.equals(ProtectDefinition.USERNAME_FIELD, ignoreCase = true)) return protectUserName + if (field.equals(ProtectDefinition.PASSWORD_FIELD, ignoreCase = true)) return protectPassword + if (field.equals(ProtectDefinition.URL_FIELD, ignoreCase = true)) return protectUrl + return if (field.equals(ProtectDefinition.NOTES_FIELD, ignoreCase = true)) protectNotes else false + + } + + object ProtectDefinition { + const val TITLE_FIELD = "Title" + const val USERNAME_FIELD = "UserName" + const val PASSWORD_FIELD = "Password" + const val URL_FIELD = "URL" + const val NOTES_FIELD = "Notes" + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index 8399b0fb4..eef5ea7a9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -232,16 +232,6 @@ public class PwDatabaseV3 extends PwDatabase { return new PwEntryV3(); } - // TODO: This could still be refactored cleaner - public void copyEncrypted(byte[] buf, int offset, int size) { - // No-op - } - - // TODO: This could still be refactored cleaner - public void copyHeader(PwDbHeaderV3 header) { - // No-op - } - @Override public boolean isBackup(PwGroupV3 group) { while (group != null) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDefsV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.kt similarity index 68% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwDefsV4.java rename to app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.kt index 0ac29a07f..0a2148c1e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDefsV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.kt @@ -17,18 +17,23 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.element; +package com.kunzisoft.keepass.database.element -public class PwDefsV4 { +abstract class PwDbHeader { - public static final String TITLE_FIELD = "Title"; + /** + * Seed that gets hashed with the userkey to form the final key + */ + var masterSeed: ByteArray? = null - public static final String USERNAME_FIELD = "UserName"; + /** + * IV used for content encryption + */ + var encryptionIV = ByteArray(16) - public static final String PASSWORD_FIELD = "Password"; + companion object { - public static final String URL_FIELD = "URL"; - - public static final String NOTES_FIELD = "Notes"; + const val PWM_DBSIG_1 = -0x655d26fd + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java index 6bdfdf1d8..3f57f5bc7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java @@ -65,8 +65,10 @@ public class PwDbHeaderV3 extends PwDbHeader { /** Size of byte buffer needed to hold this struct. */ public static final int BUF_SIZE = 124; - /** Used for the dwKeyEncRounds AES transformations */ - public byte transformSeed[] = new byte[32]; + /** + * Used for the dwKeyEncRounds AES transformations + */ + public byte[] transformSeed = new byte[32]; public int signature1; // = PWM_DBSIG_1 public int signature2; // = DBSIG_2 @@ -78,8 +80,10 @@ public class PwDbHeaderV3 extends PwDbHeader { /** Number of entries in the database */ public int numEntries; - /** SHA-256 hash of the database, used for integrity check */ - public byte contentsHash[] = new byte[32]; + /** + * SHA-256 hash of the database, used for integrity check + */ + public byte[] contentsHash = new byte[32]; public int numKeyEncRounds; @@ -88,14 +92,14 @@ public class PwDbHeaderV3 extends PwDbHeader { * @param buf * @throws IOException */ - public void loadFromFile( byte buf[], int offset ) throws IOException { - signature1 = LEDataInputStream.readInt( buf, offset + 0 ); + public void loadFromFile(byte[] buf, int offset ) throws IOException { + signature1 = LEDataInputStream.readInt( buf, offset); signature2 = LEDataInputStream.readInt( buf, offset + 4 ); flags = LEDataInputStream.readInt( buf, offset + 8 ); version = LEDataInputStream.readInt( buf, offset + 12 ); - System.arraycopy( buf, offset + 16, masterSeed, 0, 16 ); - System.arraycopy( buf, offset + 32, encryptionIV, 0, 16 ); + System.arraycopy( buf, offset + 16, getMasterSeed(), 0, 16 ); + System.arraycopy( buf, offset + 32, getEncryptionIV(), 0, 16 ); numGroups = LEDataInputStream.readInt( buf, offset + 48 ); numEntries = LEDataInputStream.readInt( buf, offset + 52 ); @@ -111,11 +115,11 @@ public class PwDbHeaderV3 extends PwDbHeader { } public PwDbHeaderV3() { - masterSeed = new byte[16]; + setMasterSeed(new byte[16]); } public static boolean matchesHeader(int sig1, int sig2) { - return (sig1 == PWM_DBSIG_1) && (sig2 == DBSIG_2); + return (sig1 == PwDbHeader.PWM_DBSIG_1) && (sig2 == DBSIG_2); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java index f11765814..e60a8b26a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java @@ -96,7 +96,7 @@ public class PwDbHeaderV4 extends PwDbHeader { public PwDbHeaderV4(PwDatabaseV4 databaseV4) { this.db = databaseV4; this.version = getMinKdbxVersion(databaseV4); // Only for writing - this.masterSeed = new byte[32]; + this.setMasterSeed(new byte[32]); } public long getVersion() { @@ -112,11 +112,11 @@ public class PwDbHeaderV4 extends PwDbHeader { boolean hasCustomData = false; @Override - public boolean operate(PwGroupV4 group) { - if (group == null) { + public boolean operate(PwGroupV4 node) { + if (node == null) { return true; } - if (group.containsCustomData()) { + if (node.containsCustomData()) { hasCustomData = true; return false; } @@ -242,7 +242,7 @@ public class PwDbHeaderV4 extends PwDbHeader { break; case PwDbHeaderV4Fields.MasterSeed: - masterSeed = fieldData; + setMasterSeed(fieldData); break; case PwDbHeaderV4Fields.TransformSeed: @@ -256,7 +256,7 @@ public class PwDbHeaderV4 extends PwDbHeader { break; case PwDbHeaderV4Fields.EncryptionIV: - encryptionIV = fieldData; + setEncryptionIV(fieldData); break; case PwDbHeaderV4Fields.InnerRandomstreamKey: @@ -319,11 +319,11 @@ public class PwDbHeaderV4 extends PwDbHeader { } int flag = LEDataInputStream.readInt(pbFlags, 0); - if ( flag < 0 || flag >= PwCompressionAlgorithm.count ) { + if ( flag < 0 || flag >= PwCompressionAlgorithm.values().length ) { throw new IOException("Unrecognized compression flag."); } - db.setCompressionAlgorithm(PwCompressionAlgorithm.fromId(flag)); + db.setCompressionAlgorithm(PwCompressionAlgorithm.Companion.fromId(flag)); } public void setRandomStreamID(byte[] streamID) throws IOException { @@ -332,11 +332,11 @@ public class PwDbHeaderV4 extends PwDbHeader { } int id = LEDataInputStream.readInt(streamID, 0); - if ( id < 0 || id >= CrsAlgorithm.count ) { + if ( id < 0 || id >= CrsAlgorithm.values().length) { throw new IOException("Invalid stream id."); } - innerRandomStream = CrsAlgorithm.fromId(id); + innerRandomStream = CrsAlgorithm.Companion.fromId(id); } /** @@ -351,7 +351,7 @@ public class PwDbHeaderV4 extends PwDbHeader { } public static boolean matchesHeader(int sig1, int sig2) { - return (sig1 == PWM_DBSIG_1) && ( (sig2 == DBSIG_PRE2) || (sig2 == DBSIG_2) ); + return (sig1 == PwDbHeader.PWM_DBSIG_1) && ( (sig2 == DBSIG_PRE2) || (sig2 == DBSIG_2) ); } public static byte[] computeHeaderHmac(byte[] header, byte[] key) throws IOException{ diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt index a35cca539..7eacb931f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt @@ -21,8 +21,6 @@ package com.kunzisoft.keepass.database.element import android.os.Parcel import android.os.Parcelable -import com.kunzisoft.keepass.database.AutoType -import com.kunzisoft.keepass.database.ExtraFields import com.kunzisoft.keepass.database.security.ProtectedBinary import com.kunzisoft.keepass.database.security.ProtectedString import com.kunzisoft.keepass.utils.MemUtil diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.kt similarity index 79% rename from app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.java rename to app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.kt index ddc89a682..75e06921d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.kt @@ -17,13 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.exception; +package com.kunzisoft.keepass.database.exception -public class ArcFourException extends InvalidDBException { - - /** - * - */ - private static final long serialVersionUID = 2103983626687861237L; +class ArcFourException : InvalidDBException() { + companion object { + private const val serialVersionUID = 2103983626687861237L + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.kt similarity index 55% rename from app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.java rename to app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.kt index 1c297218b..5d3ace33d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.kt @@ -17,31 +17,30 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.exception; +package com.kunzisoft.keepass.database.exception -import android.net.Uri; +import android.net.Uri -import com.kunzisoft.keepass.utils.EmptyUtils; +import com.kunzisoft.keepass.utils.EmptyUtils -import java.io.FileNotFoundException; +import java.io.FileNotFoundException /** * Created by bpellin on 3/14/16. */ -public class ContentFileNotFoundException extends FileNotFoundException { - public static FileNotFoundException getInstance(Uri uri) { - if (uri == null) { return new FileNotFoundException(); } +class ContentFileNotFoundException : FileNotFoundException() { + companion object { + fun getInstance(uri: Uri?): FileNotFoundException { + if (uri == null) { + return FileNotFoundException() + } - String scheme = uri.getScheme(); + val scheme = uri.scheme + + return if (!EmptyUtils.isNullOrEmpty(scheme) && scheme!!.equals("content", ignoreCase = true)) { + ContentFileNotFoundException() + } else FileNotFoundException() - if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("content")) { - return new ContentFileNotFoundException(); } - - return new FileNotFoundException(); - } - - public ContentFileNotFoundException() { - super(); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.java deleted file mode 100644 index bf389ab0f..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.exception; - -public class InvalidAlgorithmException extends InvalidDBException { - /** - * - */ - private static final long serialVersionUID = 3062682891863487208L; - - public InvalidAlgorithmException() { - super(); - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.kt new file mode 100644 index 000000000..3a784f2d3 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.exception + +class InvalidAlgorithmException : InvalidDBException() { + companion object { + private const val serialVersionUID = 3062682891863487208L + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.kt similarity index 74% rename from app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.java rename to app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.kt index 6b65c7bf6..4d836b281 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.kt @@ -17,16 +17,16 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.exception; +package com.kunzisoft.keepass.database.exception -public class InvalidDBSignatureException extends InvalidDBException { - /** - * - */ - private static final long serialVersionUID = -5358923878743513758L; +open class InvalidDBException : Exception { - public InvalidDBSignatureException() { - super(); - } + constructor(str: String) : super(str) + + constructor() : super() + + companion object { + private const val serialVersionUID = 5191964825154190923L + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InconsistentDBException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.kt similarity index 76% rename from app/src/main/java/com/kunzisoft/keepass/database/exception/InconsistentDBException.java rename to app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.kt index 317f9f803..dd312b91a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InconsistentDBException.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.kt @@ -17,14 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.exception; +package com.kunzisoft.keepass.database.exception -public class InconsistentDBException extends Exception { - - public InconsistentDBException(String msg) { - super(msg); - } - - private static final long serialVersionUID = 4879502365625912291L; +class InvalidDBSignatureException : InvalidDBException() { + companion object { + private const val serialVersionUID = -5358923878743513758L + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.java deleted file mode 100644 index 8f9f2f04b..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.exception; - -public class InvalidDBVersionException extends InvalidDBException { - /** - * - */ - private static final long serialVersionUID = -4260650987856400586L; - - public InvalidDBVersionException() { - super(); - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.kt new file mode 100644 index 000000000..43f558389 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.exception + +class InvalidDBVersionException : InvalidDBException() { + companion object { + private const val serialVersionUID = -4260650987856400586L + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.kt similarity index 75% rename from app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.java rename to app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.kt index 88c5db652..44bfadbcf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.kt @@ -16,15 +16,10 @@ * You should have received a copy of the GNU General Public License * along with KeePass DX. If not, see . * - */package com.kunzisoft.keepass.database.exception; + */package com.kunzisoft.keepass.database.exception -public class InvalidKeyFileException extends InvalidDBException { - /** - * - */ - private static final long serialVersionUID = 5540694419562294464L; - - public InvalidKeyFileException() { - super(); - } +open class InvalidKeyFileException : InvalidDBException() { + companion object { + private const val serialVersionUID = 5540694419562294464L + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.java deleted file mode 100644 index 0b5f475c9..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.exception; - -public class InvalidPasswordException extends InvalidDBException { - - /** - * - */ - private static final long serialVersionUID = -8729476180242058319L; - - public InvalidPasswordException(String str) { - super(str); - } - - public InvalidPasswordException() { - super(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.kt new file mode 100644 index 000000000..967ef7f77 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.exception + +class InvalidPasswordException : InvalidDBException() { + companion object { + private const val serialVersionUID = -8729476180242058319L + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.kt new file mode 100644 index 000000000..9688a5251 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.exception + +class KeyFileEmptyException : InvalidKeyFileException() { + companion object { + private const val serialVersionUID = -1630780661204212325L + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.java deleted file mode 100644 index e82c6fdfa..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.exception; - -public class PwDbOutputException extends Exception { - public PwDbOutputException(String string) { - super(string); - } - - public PwDbOutputException(String string, Exception e) { super(string, e); } - - public PwDbOutputException(Exception e) { - super(e); - } - - /** - * - */ - private static final long serialVersionUID = 3321212743159473368L; -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.kt similarity index 69% rename from app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.java rename to app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.kt index 3e42b7167..ccd06efa9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.kt @@ -17,21 +17,16 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.exception; +package com.kunzisoft.keepass.database.exception -public class InvalidDBException extends Exception { +class PwDbOutputException : Exception { + constructor(string: String) : super(string) - public InvalidDBException(String str) { - super(str); - } + constructor(string: String, e: Exception) : super(string, e) - public InvalidDBException() { - super(); - } - - /** - * - */ - private static final long serialVersionUID = 5191964825154190923L; + constructor(e: Exception) : super(e) + companion object { + private const val serialVersionUID = 3321212743159473368L + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.java deleted file mode 100644 index 0bf93fe3f..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.exception; - -public class SamsungClipboardException extends Exception { - - public SamsungClipboardException(String message) { - super(message); - } - - public SamsungClipboardException(Exception e) { - super(e); - } - - /** - * - */ - private static final long serialVersionUID = -3168837280393843509L; - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.kt similarity index 75% rename from app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.java rename to app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.kt index 1926f6ca0..74f08db0b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.kt @@ -17,15 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.exception; +package com.kunzisoft.keepass.database.exception -public class KeyFileEmptyException extends InvalidKeyFileException { - /** - * - */ - private static final long serialVersionUID = -1630780661204212325L; +class SamsungClipboardException(e: Exception) : Exception(e) { + companion object { + private const val serialVersionUID = -3168837280393843509L + } - public KeyFileEmptyException() { - super(); - } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.java b/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.java deleted file mode 100644 index 917e23e57..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.kunzisoft.keepass.database.exception; - -import java.io.IOException; - -public class UnknownKDF extends IOException { - - private static String message = "Unknown key derivation function"; - - public UnknownKDF() { - super(message); - } - - public UnknownKDF(Exception e) { - super(message, e); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt new file mode 100644 index 000000000..2897148c5 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt @@ -0,0 +1,9 @@ +package com.kunzisoft.keepass.database.exception + +import java.io.IOException + +class UnknownKDF : IOException(message) { + companion object { + private const val message = "Unknown key derivation function" + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java deleted file mode 100644 index 945865092..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.iterator; - -import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.database.search.SearchParameters; - -import java.util.NoSuchElementException; - -public class EntrySearchStringIteratorV3 extends EntrySearchStringIterator { - - private PwEntryV3 mEntry; - private SearchParameters mSearchParameters; - - public EntrySearchStringIteratorV3(PwEntryV3 entry) { - this(entry, new SearchParameters()); - } - - public EntrySearchStringIteratorV3(PwEntryV3 entry, SearchParameters searchParameters) { - this.mEntry = entry; - this.mSearchParameters = searchParameters; - } - - private static final int title = 0; - private static final int url = 1; - private static final int username = 2; - private static final int comment = 3; - private static final int maxEntries = 4; - - private int current = 0; - - @Override - public boolean hasNext() { - return current < maxEntries; - } - - @Override - public String next() { - // Past the end of the list - if (current == maxEntries) { - throw new NoSuchElementException("Past final string"); - } - - useSearchParameters(); - - String str = getCurrentString(); - current++; - return str; - } - - private void useSearchParameters() { - - if (mSearchParameters == null) { return; } - - boolean found = false; - while (!found) { - switch (current) { - case title: - found = mSearchParameters.getSearchInTitles(); - break; - case url: - found = mSearchParameters.getSearchInUrls(); - break; - case username: - found = mSearchParameters.getSearchInUserNames(); - break; - case comment: - found = mSearchParameters.getSearchInNotes(); - break; - default: - found = true; - } - - if (!found) { current++; } - } - } - - private String getCurrentString() { - switch (current) { - case title: - return mEntry.getTitle(); - case url: - return mEntry.getUrl(); - case username: - return mEntry.getUsername(); - case comment: - return mEntry.getNotes(); - default: - return ""; - } - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.kt new file mode 100644 index 000000000..2ff81e61b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.iterator + +import com.kunzisoft.keepass.database.element.PwEntryV3 +import com.kunzisoft.keepass.database.search.SearchParameters + +import java.util.NoSuchElementException + +class EntrySearchStringIteratorV3 + +@JvmOverloads +constructor(private val mEntry: PwEntryV3, + private val mSearchParameters: SearchParameters? = SearchParameters()) + : EntrySearchStringIterator() { + + private var current = 0 + + private val currentString: String + get() { + return when (current) { + title -> mEntry.title + url -> mEntry.url + username -> mEntry.username + comment -> mEntry.notes + else -> "" + } + } + + override fun hasNext(): Boolean { + return current < maxEntries + } + + override fun next(): String { + // Past the end of the list + if (current == maxEntries) { + throw NoSuchElementException("Past final string") + } + + useSearchParameters() + + val str = currentString + current++ + return str + } + + private fun useSearchParameters() { + + if (mSearchParameters == null) { + return + } + + var found = false + while (!found) { + found = when (current) { + title -> mSearchParameters.searchInTitles + url -> mSearchParameters.searchInUrls + username -> mSearchParameters.searchInUserNames + comment -> mSearchParameters.searchInNotes + else -> true + } + + if (!found) { + current++ + } + } + } + + companion object { + + private const val title = 0 + private const val url = 1 + private const val username = 2 + private const val comment = 3 + private const val maxEntries = 4 + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt similarity index 50% rename from app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java rename to app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt index 1b60c2e88..93ad10914 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt @@ -17,28 +17,28 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.load; +package com.kunzisoft.keepass.database.load -import com.kunzisoft.keepass.database.element.PwDatabase; -import com.kunzisoft.keepass.database.exception.InvalidDBException; -import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; +import com.kunzisoft.keepass.database.element.PwDatabase +import com.kunzisoft.keepass.database.exception.InvalidDBException +import com.kunzisoft.keepass.tasks.ProgressTaskUpdater -import java.io.IOException; -import java.io.InputStream; +import java.io.IOException +import java.io.InputStream -public abstract class Importer { +abstract class Importer> { - /** - * Load a versioned database file, return contents in a new PwDatabase. - * - * @param inStream Existing file to load. - * @param password Pass phrase for infile. - * @return new PwDatabase container. - * - * @throws IOException on any file error. - * @throws InvalidDBException on database error. - */ - public abstract PwDb openDatabase(InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater updater) - throws IOException, InvalidDBException; + /** + * Load a versioned database file, return contents in a new PwDatabase. + * + * @param inStream Existing file to load. + * @param password Pass phrase for infile. + * @return new PwDatabase container. + * + * @throws IOException on any file error. + * @throws InvalidDBException on database error. + */ + @Throws(IOException::class, InvalidDBException::class) + abstract fun openDatabase(inStream: InputStream, password: String?, keyInputStream: InputStream?, updater: ProgressTaskUpdater?): PwDb } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java index 966166e9b..3ece28c78 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java @@ -45,6 +45,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA package com.kunzisoft.keepass.database.load; +import android.support.annotation.NonNull; import android.util.Log; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.crypto.CipherFactory; @@ -76,8 +77,9 @@ public class ImporterV3 extends Importer { private PwDatabaseV3 databaseToOpen; + @NonNull @Override - public PwDatabaseV3 openDatabase(InputStream inStream, + public PwDatabaseV3 openDatabase(@NonNull InputStream inStream, String password, InputStream kfIs, ProgressTaskUpdater progressTaskUpdater) @@ -116,14 +118,11 @@ public class ImporterV3 extends Importer { } else { throw new InvalidAlgorithmException(); } - - // Copy for testing - databaseToOpen.copyHeader(hdr); databaseToOpen.setNumberKeyEncryptionRounds(hdr.numKeyEncRounds); // Generate transformedMasterKey from masterKey - databaseToOpen.makeFinalKey(hdr.masterSeed, hdr.transformSeed, databaseToOpen.getNumberKeyEncryptionRounds()); + databaseToOpen.makeFinalKey(hdr.getMasterSeed(), hdr.transformSeed, databaseToOpen.getNumberKeyEncryptionRounds()); if (progressTaskUpdater != null) progressTaskUpdater.updateMessage(R.string.decrypting_db); @@ -145,7 +144,7 @@ public class ImporterV3 extends Importer { } try { - cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( databaseToOpen.getFinalKey(), "AES" ), new IvParameterSpec( hdr.encryptionIV ) ); + cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( databaseToOpen.getFinalKey(), "AES" ), new IvParameterSpec(hdr.getEncryptionIV()) ); } catch (InvalidKeyException e1) { throw new IOException("Invalid key"); } catch (InvalidAlgorithmParameterException e1) { @@ -164,9 +163,6 @@ public class ImporterV3 extends Importer { throw new InvalidPasswordException(); } - // Copy decrypted data for testing - databaseToOpen.copyEncrypted(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize); - MessageDigest md = null; try { md = MessageDigest.getInstance("SHA-256"); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java index 3a22633b8..b865feace 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java @@ -19,6 +19,8 @@ */ package com.kunzisoft.keepass.database.load; +import android.support.annotation.NonNull; + import biz.source_code.base64Coder.Base64Coder; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.crypto.CipherFactory; @@ -72,8 +74,9 @@ public class ImporterV4 extends Importer { this.streamDir = streamDir; } + @NonNull @Override - public PwDatabaseV4 openDatabase(InputStream inStream, + public PwDatabaseV4 openDatabase(@NonNull InputStream inStream, String password, InputStream keyInputStream, ProgressTaskUpdater progressTaskUpdater) @@ -93,7 +96,7 @@ public class ImporterV4 extends Importer { byte[] pbHeader = hh.header; mDatabase.retrieveMasterKey(password, keyInputStream); - mDatabase.makeFinalKey(header.masterSeed); + mDatabase.makeFinalKey(header.getMasterSeed()); if (progressTaskUpdater != null) progressTaskUpdater.updateMessage(R.string.decrypting_db); @@ -103,7 +106,7 @@ public class ImporterV4 extends Importer { engine = CipherFactory.getInstance(mDatabase.getDataCipher()); mDatabase.setDataEngine(engine); mDatabase.setEncryptionAlgorithm(engine.getPwEncryptionAlgorithm()); - cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.getFinalKey(), header.encryptionIV); + cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.getFinalKey(), header.getEncryptionIV()); } catch (NoSuchAlgorithmException|NoSuchPaddingException|InvalidKeyException|InvalidAlgorithmParameterException e) { throw new IOException("Invalid algorithm.", e); } @@ -432,17 +435,17 @@ public class ImporterV4 extends Importer { case MemoryProtection: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtTitle) ) { - mDatabase.getMemoryProtection().protectTitle = ReadBool(xpp, false); + mDatabase.getMemoryProtection().setProtectTitle(ReadBool(xpp, false)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtUserName) ) { - mDatabase.getMemoryProtection().protectUserName = ReadBool(xpp, false); + mDatabase.getMemoryProtection().setProtectUserName(ReadBool(xpp, false)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtPassword) ) { - mDatabase.getMemoryProtection().protectPassword = ReadBool(xpp, false); + mDatabase.getMemoryProtection().setProtectPassword(ReadBool(xpp, false)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtURL) ) { - mDatabase.getMemoryProtection().protectUrl = ReadBool(xpp, false); + mDatabase.getMemoryProtection().setProtectUrl(ReadBool(xpp, false)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtNotes) ) { - mDatabase.getMemoryProtection().protectNotes = ReadBool(xpp, false); + mDatabase.getMemoryProtection().setProtectNotes(ReadBool(xpp, false)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtAutoHide) ) { - mDatabase.getMemoryProtection().autoEnableVisualHiding = ReadBool(xpp, false); + mDatabase.getMemoryProtection().setAutoEnableVisualHiding(ReadBool(xpp, false)); } else { ReadUnknown(xpp); } @@ -687,11 +690,11 @@ public class ImporterV4 extends Importer { case EntryAutoType: if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeEnabled) ) { - ctxEntry.getAutoType().enabled = ReadBool(xpp, true); + ctxEntry.getAutoType().setEnabled(ReadBool(xpp, true)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeObfuscation) ) { - ctxEntry.getAutoType().obfuscationOptions = ReadUInt(xpp, 0); + ctxEntry.getAutoType().setObfuscationOptions(ReadUInt(xpp, 0)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeDefaultSeq) ) { - ctxEntry.getAutoType().defaultSequence = ReadString(xpp); + ctxEntry.getAutoType().setDefaultSequence(ReadString(xpp)); } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeItem) ) { return SwitchContext(ctx, KdbContext.EntryAutoTypeItem, xpp); } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java index 687af8b43..ad40de81d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java @@ -39,8 +39,8 @@ public class PwDbHeaderOutputV3 { mOS.write(LEDataOutputStream.writeIntBuf(mHeader.signature2)); mOS.write(LEDataOutputStream.writeIntBuf(mHeader.flags)); mOS.write(LEDataOutputStream.writeIntBuf(mHeader.version)); - mOS.write(mHeader.masterSeed); - mOS.write(mHeader.encryptionIV); + mOS.write(mHeader.getMasterSeed()); + mOS.write(mHeader.getEncryptionIV()); mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numGroups)); mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numEntries)); } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java index abeb66e70..eee7b7b2a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java @@ -63,7 +63,7 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput { } try { - d.makeFinalKey(header.masterSeed); + d.makeFinalKey(header.getMasterSeed()); } catch (IOException e) { throw new PwDbOutputException(e); } @@ -92,8 +92,8 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput { writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CipherID, Types.UUIDtoBytes(db.getDataCipher())); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.getCompressionAlgorithm().id)); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.MasterSeed, header.masterSeed); + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.getCompressionAlgorithm().getId())); + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.MasterSeed, header.getMasterSeed()); if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformSeed, header.getTransformSeed()); @@ -102,14 +102,14 @@ public class PwDbHeaderOutputV4 extends PwDbHeaderOutput { writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.getKdfParameters())); } - if (header.encryptionIV.length > 0) { - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV); + if (header.getEncryptionIV().length > 0) { + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EncryptionIV, header.getEncryptionIV()); } if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey); writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomStreamID, LEDataOutputStream.writeIntBuf(header.innerRandomStream.id)); + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomStreamID, LEDataOutputStream.writeIntBuf(header.innerRandomStream.getId())); } if (db.containsPublicCustomData()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java index 836330391..851b72987 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java @@ -43,7 +43,7 @@ public class PwDbInnerHeaderOutputV4 { public void output() throws IOException { los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID); los.writeInt(4); - los.writeInt(header.innerRandomStream.id); + los.writeInt(header.innerRandomStream.getId()); int streamKeySize = header.innerRandomStreamKey.length; los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java index 2cbcca5d8..6b8d5ab89 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java @@ -41,8 +41,8 @@ public abstract class PwDbOutput
{ } catch (NoSuchAlgorithmException e) { throw new PwDbOutputException("Does not support secure random number generation."); } - random.nextBytes(header.encryptionIV); - random.nextBytes(header.masterSeed); + random.nextBytes(header.getEncryptionIV()); + random.nextBytes(header.getMasterSeed()); return random; } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java index 017f7340f..b8cda3f05 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java @@ -50,7 +50,7 @@ public class PwDbV3Output extends PwDbOutput { public byte[] getFinalKey(PwDbHeader header) throws PwDbOutputException { try { PwDbHeaderV3 h3 = (PwDbHeaderV3) header; - mDatabaseV3.makeFinalKey(h3.masterSeed, h3.transformSeed, mDatabaseV3.getNumberKeyEncryptionRounds()); + mDatabaseV3.makeFinalKey(h3.getMasterSeed(), h3.transformSeed, mDatabaseV3.getNumberKeyEncryptionRounds()); return mDatabaseV3.getFinalKey(); } catch (IOException e) { throw new PwDbOutputException("Key creation failed.", e); @@ -81,7 +81,7 @@ public class PwDbV3Output extends PwDbOutput { } try { - cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(finalKey, "AES" ), new IvParameterSpec(header.encryptionIV) ); + cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(finalKey, "AES" ), new IvParameterSpec(header.getEncryptionIV()) ); CipherOutputStream cos = new CipherOutputStream(mOS, cipher); BufferedOutputStream bos = new BufferedOutputStream(cos); outputPlanGroupAndEntries(bos); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java index 036533337..1b9e6f8f1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java @@ -153,12 +153,12 @@ public class PwDbV4Output extends PwDbOutput { }, new NodeHandler() { @Override - public boolean operate(PwGroupV4 group) { + public boolean operate(PwGroupV4 node) { while (true) { try { - if (group.getParent() == groupStack.peek()) { - groupStack.push(group); - startGroup(group); + if (node.getParent() == groupStack.peek()) { + groupStack.push(node); + startGroup(node); break; } else { groupStack.pop(); @@ -239,7 +239,7 @@ public class PwDbV4Output extends PwDbOutput { try { //mPM.makeFinalKey(header.masterSeed, mPM.kdfParameters); - cipher = engine.getCipher(Cipher.ENCRYPT_MODE, mPM.getFinalKey(), header.encryptionIV); + cipher = engine.getCipher(Cipher.ENCRYPT_MODE, mPM.getFinalKey(), header.getEncryptionIV()); } catch (Exception e) { throw new PwDbOutputException("Invalid algorithm.", e); } @@ -250,13 +250,13 @@ public class PwDbV4Output extends PwDbOutput { @Override protected SecureRandom setIVs(PwDbHeaderV4 header) throws PwDbOutputException { SecureRandom random = super.setIVs(header); - random.nextBytes(header.masterSeed); + random.nextBytes(header.getMasterSeed()); int ivLength = engine.ivLength(); - if (ivLength != header.encryptionIV.length) { - header.encryptionIV = new byte[ivLength]; + if (ivLength != header.getEncryptionIV().length) { + header.setEncryptionIV(new byte[ivLength]); } - random.nextBytes(header.encryptionIV); + random.nextBytes(header.getEncryptionIV()); if (mPM.getKdfParameters() == null) { mPM.setKdfParameters(KdfFactory.aesKdf.getDefaultParameters()); @@ -539,11 +539,11 @@ public class PwDbV4Output extends PwDbOutput { xml.startTag(null, name); - writeObject(PwDatabaseV4XML.ElemAutoTypeEnabled, autoType.enabled); - writeObject(PwDatabaseV4XML.ElemAutoTypeObfuscation, autoType.obfuscationOptions); + writeObject(PwDatabaseV4XML.ElemAutoTypeEnabled, autoType.getEnabled()); + writeObject(PwDatabaseV4XML.ElemAutoTypeObfuscation, autoType.getObfuscationOptions()); - if (autoType.defaultSequence.length() > 0) { - writeObject(PwDatabaseV4XML.ElemAutoTypeDefaultSeq, autoType.defaultSequence, true); + if (autoType.getDefaultSequence().length() > 0) { + writeObject(PwDatabaseV4XML.ElemAutoTypeDefaultSeq, autoType.getDefaultSequence(), true); } for (Entry pair : autoType.entrySet()) { @@ -575,20 +575,20 @@ public class PwDbV4Output extends PwDbOutput { xml.startTag(null, PwDatabaseV4XML.ElemValue); boolean protect = value.isProtected(); if (isEntryString) { - if (key.equals(PwDefsV4.TITLE_FIELD)) { - protect = mPM.getMemoryProtection().protectTitle; + if (key.equals(MemoryProtectionConfig.ProtectDefinition.TITLE_FIELD)) { + protect = mPM.getMemoryProtection().getProtectTitle(); } - else if (key.equals(PwDefsV4.USERNAME_FIELD)) { - protect = mPM.getMemoryProtection().protectUserName; + else if (key.equals(MemoryProtectionConfig.ProtectDefinition.USERNAME_FIELD)) { + protect = mPM.getMemoryProtection().getProtectUserName(); } - else if (key.equals(PwDefsV4.PASSWORD_FIELD)) { - protect = mPM.getMemoryProtection().protectPassword; + else if (key.equals(MemoryProtectionConfig.ProtectDefinition.PASSWORD_FIELD)) { + protect = mPM.getMemoryProtection().getProtectPassword(); } - else if (key.equals(PwDefsV4.URL_FIELD)) { - protect = mPM.getMemoryProtection().protectUrl; + else if (key.equals(MemoryProtectionConfig.ProtectDefinition.URL_FIELD)) { + protect = mPM.getMemoryProtection().getProtectUrl(); } - else if (key.equals(PwDefsV4.NOTES_FIELD)) { - protect = mPM.getMemoryProtection().protectNotes; + else if (key.equals(MemoryProtectionConfig.ProtectDefinition.NOTES_FIELD)) { + protect = mPM.getMemoryProtection().getProtectNotes(); } } @@ -651,11 +651,11 @@ public class PwDbV4Output extends PwDbOutput { xml.startTag(null, name); - writeObject(PwDatabaseV4XML.ElemProtTitle, value.protectTitle); - writeObject(PwDatabaseV4XML.ElemProtUserName, value.protectUserName); - writeObject(PwDatabaseV4XML.ElemProtPassword, value.protectPassword); - writeObject(PwDatabaseV4XML.ElemProtURL, value.protectUrl); - writeObject(PwDatabaseV4XML.ElemProtNotes, value.protectNotes); + writeObject(PwDatabaseV4XML.ElemProtTitle, value.getProtectTitle()); + writeObject(PwDatabaseV4XML.ElemProtUserName, value.getProtectUserName()); + writeObject(PwDatabaseV4XML.ElemProtPassword, value.getProtectPassword()); + writeObject(PwDatabaseV4XML.ElemProtURL, value.getProtectUrl()); + writeObject(PwDatabaseV4XML.ElemProtNotes, value.getProtectNotes()); xml.endTag(null, name); diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java deleted file mode 100644 index b2142b206..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.search; - -import com.kunzisoft.keepass.database.NodeHandler; -import com.kunzisoft.keepass.database.element.PwEntryV4; -import com.kunzisoft.keepass.database.element.PwGroupV4; -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator; -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4; -import com.kunzisoft.keepass.utils.StringUtil; -import com.kunzisoft.keepass.utils.UuidUtil; - -import java.util.Date; -import java.util.List; -import java.util.Locale; - -public class EntrySearchHandlerV4 extends NodeHandler { - - private List mListStorage; - private SearchParametersV4 mSearchParametersV4; - protected Date now; - - public EntrySearchHandlerV4(SearchParametersV4 searchParametersV4, List listStorage) { - this.mListStorage = listStorage; - this.mSearchParametersV4 = searchParametersV4; - this.now = new Date(); - } - - @Override - public boolean operate(PwEntryV4 entry) { - if (mSearchParametersV4.getRespectEntrySearchingDisabled() && !entry.isSearchingEnabled()) { - return true; - } - - if (mSearchParametersV4.getExcludeExpired() && entry.isExpires() && now.after(entry.getExpiryTime().getDate())) { - return true; - } - - String term = mSearchParametersV4.getSearchString(); - if (mSearchParametersV4.getIgnoreCase()) { - term = term.toLowerCase(); - } - - if (searchStrings(entry, term)) { - mListStorage.add(entry); - return true; - } - - if (mSearchParametersV4.getSearchInGroupNames()) { - PwGroupV4 parent = entry.getParent(); - if (parent != null) { - String groupName = parent.getTitle(); - if (mSearchParametersV4.getIgnoreCase()) { - groupName = groupName.toLowerCase(); - } - - if (groupName.contains(term)) { - mListStorage.add(entry); - return true; - } - } - } - - if (searchID(entry)) { - mListStorage.add(entry); - return true; - } - - return true; - } - - private boolean searchID(PwEntryV4 entry) { - if (mSearchParametersV4.getSearchInUUIDs()) { - String hex = UuidUtil.toHexString(entry.getId()); - return StringUtil.INSTANCE.indexOfIgnoreCase(hex, mSearchParametersV4.getSearchString(), Locale.ENGLISH) >= 0; - } - - return false; - } - - private boolean searchStrings(PwEntryV4 entry, String term) { - EntrySearchStringIterator iterator = new EntrySearchStringIteratorV4(entry, mSearchParametersV4); - while (iterator.hasNext()) { - String str = iterator.next(); - if (str.length() > 0) { - if (mSearchParametersV4.getIgnoreCase()) { - str = str.toLowerCase(); - } - - if (str.contains(term)) { - return true; - } - } - } - - return false; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt new file mode 100644 index 000000000..4b5d3b41b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.search + +import com.kunzisoft.keepass.database.NodeHandler +import com.kunzisoft.keepass.database.element.PwEntryV4 +import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4 +import com.kunzisoft.keepass.utils.StringUtil +import com.kunzisoft.keepass.utils.UuidUtil + +import java.util.Date +import java.util.Locale + +class EntrySearchHandlerV4(private val mSearchParametersV4: SearchParametersV4, private val mListStorage: MutableList) : NodeHandler() { + + private var now: Date = Date() + + override fun operate(node: PwEntryV4): Boolean { + if (mSearchParametersV4.respectEntrySearchingDisabled && !node.isSearchingEnabled) { + return true + } + + if (mSearchParametersV4.excludeExpired && node.isExpires && now.after(node.expiryTime.date)) { + return true + } + + var term = mSearchParametersV4.searchString + if (mSearchParametersV4.ignoreCase) { + term = term.toLowerCase() + } + + if (searchStrings(node, term)) { + mListStorage.add(node) + return true + } + + if (mSearchParametersV4.searchInGroupNames) { + val parent = node.parent + if (parent != null) { + var groupName = parent.title + if (mSearchParametersV4.ignoreCase) { + groupName = groupName.toLowerCase() + } + + if (groupName.contains(term)) { + mListStorage.add(node) + return true + } + } + } + + if (searchID(node)) { + mListStorage.add(node) + return true + } + + return true + } + + private fun searchID(entry: PwEntryV4): Boolean { + if (mSearchParametersV4.searchInUUIDs) { + val hex = UuidUtil.toHexString(entry.id) + return StringUtil.indexOfIgnoreCase(hex, mSearchParametersV4.searchString, Locale.ENGLISH) >= 0 + } + + return false + } + + private fun searchStrings(entry: PwEntryV4, term: String): Boolean { + val iterator = EntrySearchStringIteratorV4(entry, mSearchParametersV4) + while (iterator.hasNext()) { + var str = iterator.next() + if (str.isNotEmpty()) { + if (mSearchParametersV4.ignoreCase) { + str = str.toLowerCase() + } + + if (str.contains(term)) { + return true + } + } + } + + return false + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt index 422572359..a963eae38 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt @@ -60,10 +60,10 @@ class SearchDbHelper(private val isOmitBackup: Boolean) { } }, object : NodeHandler() { - override fun operate(group: GroupVersioned): Boolean { + override fun operate(node: GroupVersioned): Boolean { return when { incrementEntry >= max -> false - database.isGroupSearchable(group, isOmitBackup) -> true + database.isGroupSearchable(node, isOmitBackup) -> true else -> incrementEntry < max } } From 8890296beb505f7dee8fd4431c15ff1782f6042c Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 2 Jun 2019 14:04:36 +0200 Subject: [PATCH 117/289] Kotlinized importers --- .../keepass/database/load/Importer.kt | 4 +- .../keepass/database/load/ImporterV3.java | 338 ----- .../keepass/database/load/ImporterV3.kt | 297 +++++ .../keepass/database/load/ImporterV4.java | 1139 ----------------- .../keepass/database/load/ImporterV4.kt | 1071 ++++++++++++++++ 5 files changed, 1370 insertions(+), 1479 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt index 93ad10914..5f8f1866a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt @@ -31,7 +31,7 @@ abstract class Importer> { /** * Load a versioned database file, return contents in a new PwDatabase. * - * @param inStream Existing file to load. + * @param databaseInputStream Existing file to load. * @param password Pass phrase for infile. * @return new PwDatabase container. * @@ -39,6 +39,6 @@ abstract class Importer> { * @throws InvalidDBException on database error. */ @Throws(IOException::class, InvalidDBException::class) - abstract fun openDatabase(inStream: InputStream, password: String?, keyInputStream: InputStream?, updater: ProgressTaskUpdater?): PwDb + abstract fun openDatabase(databaseInputStream: InputStream, password: String?, keyInputStream: InputStream?, progressTaskUpdater: ProgressTaskUpdater?): PwDb } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java deleted file mode 100644 index 3ece28c78..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - * - -Derived from - -KeePass for J2ME - -Copyright 2007 Naomaru Itoi - -This file was derived from - -Java clone of KeePass - A KeePass file viewer for Java -Copyright 2006 Bill Zwicky - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; version 2 - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package com.kunzisoft.keepass.database.load; - -import android.support.annotation.NonNull; -import android.util.Log; -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.element.*; -import com.kunzisoft.keepass.database.exception.*; -import com.kunzisoft.keepass.stream.LEDataInputStream; -import com.kunzisoft.keepass.stream.NullOutputStream; -import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; -import com.kunzisoft.keepass.utils.Types; - -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.security.*; -import java.util.Arrays; - -/** - * Load a v3 database file. - * - * @author Naomaru Itoi - * @author Bill Zwicky - */ -public class ImporterV3 extends Importer { - - private static final String TAG = ImporterV3.class.getName(); - - private PwDatabaseV3 databaseToOpen; - - @NonNull - @Override - public PwDatabaseV3 openDatabase(@NonNull InputStream inStream, - String password, - InputStream kfIs, - ProgressTaskUpdater progressTaskUpdater) - throws IOException, InvalidDBException { - - // Load entire file, most of it's encrypted. - int fileSize = inStream.available(); - byte[] filebuf = new byte[fileSize + 16]; // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer - inStream.read(filebuf, 0, fileSize); // TODO remove - inStream.close(); - - // Parse header (unencrypted) - if( fileSize < PwDbHeaderV3.BUF_SIZE ) - throw new IOException( "File too short for header" ); - PwDbHeaderV3 hdr = new PwDbHeaderV3(); - hdr.loadFromFile(filebuf, 0 ); - - if( (hdr.signature1 != PwDbHeader.PWM_DBSIG_1) || (hdr.signature2 != PwDbHeaderV3.DBSIG_2) ) { - throw new InvalidDBSignatureException(); - } - - if( !hdr.matchesVersion() ) { - throw new InvalidDBVersionException(); - } - - if (progressTaskUpdater != null) - progressTaskUpdater.updateMessage(R.string.retrieving_db_key); - databaseToOpen = new PwDatabaseV3(); - databaseToOpen.retrieveMasterKey(password, kfIs); - - // Select algorithm - if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) { - databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.AESRijndael); - } else if( (hdr.flags & PwDbHeaderV3.FLAG_TWOFISH) != 0 ) { - databaseToOpen.setEncryptionAlgorithm(PwEncryptionAlgorithm.Twofish); - } else { - throw new InvalidAlgorithmException(); - } - - databaseToOpen.setNumberKeyEncryptionRounds(hdr.numKeyEncRounds); - - // Generate transformedMasterKey from masterKey - databaseToOpen.makeFinalKey(hdr.getMasterSeed(), hdr.transformSeed, databaseToOpen.getNumberKeyEncryptionRounds()); - - if (progressTaskUpdater != null) - progressTaskUpdater.updateMessage(R.string.decrypting_db); - // Initialize Rijndael algorithm - Cipher cipher; - try { - if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AESRijndael) { - cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); - } else if ( databaseToOpen.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) { - cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING"); - } else { - throw new IOException( "Encryption algorithm is not supported" ); - } - - } catch (NoSuchAlgorithmException e1) { - throw new IOException("No such algorithm"); - } catch (NoSuchPaddingException e1) { - throw new IOException("No such pdading"); - } - - try { - cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( databaseToOpen.getFinalKey(), "AES" ), new IvParameterSpec(hdr.getEncryptionIV()) ); - } catch (InvalidKeyException e1) { - throw new IOException("Invalid key"); - } catch (InvalidAlgorithmParameterException e1) { - throw new IOException("Invalid algorithm parameter."); - } - - // Decrypt! The first bytes aren't encrypted (that's the header) - int encryptedPartSize; - try { - encryptedPartSize = cipher.doFinal(filebuf, PwDbHeaderV3.BUF_SIZE, fileSize - PwDbHeaderV3.BUF_SIZE, filebuf, PwDbHeaderV3.BUF_SIZE ); - } catch (ShortBufferException e1) { - throw new IOException("Buffer too short"); - } catch (IllegalBlockSizeException e1) { - throw new IOException("Invalid block size"); - } catch (BadPaddingException e1) { - throw new InvalidPasswordException(); - } - - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IOException("No SHA-256 algorithm"); - } - NullOutputStream nos = new NullOutputStream(); - DigestOutputStream dos = new DigestOutputStream(nos, md); - dos.write(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize); - dos.close(); - byte[] hash = md.digest(); - - if( ! Arrays.equals(hash, hdr.contentsHash) ) { - - Log.w(TAG,"Database file did not decrypt correctly. (checksum code is broken)"); - throw new InvalidPasswordException(); - } - - // New manual root because V3 contains multiple root groups (here available with getRootGroups()) - PwGroupV3 newRoot = databaseToOpen.createGroup(); - newRoot.setLevel(-1); - databaseToOpen.setRootGroup(newRoot); - - // Import all groups - int pos = PwDbHeaderV3.BUF_SIZE; - PwGroupV3 newGrp = databaseToOpen.createGroup(); - for( int i = 0; i < hdr.numGroups; ) { - int fieldType = LEDataInputStream.readUShort( filebuf, pos ); - pos += 2; - int fieldSize = LEDataInputStream.readInt( filebuf, pos ); - pos += 4; - - if( fieldType == 0xFFFF ) { - // End-Group record. Save group and count it. - databaseToOpen.addGroupIndex(newGrp); - newGrp = databaseToOpen.createGroup(); - i++; - } - else { - readGroupField(databaseToOpen, newGrp, fieldType, filebuf, pos); - } - pos += fieldSize; - } - - // Import all entries - PwEntryV3 newEnt = databaseToOpen.createEntry(); - for( int i = 0; i < hdr.numEntries; ) { - int fieldType = LEDataInputStream.readUShort( filebuf, pos ); - int fieldSize = LEDataInputStream.readInt( filebuf, pos + 2 ); - - if( fieldType == 0xFFFF ) { - // End-Group record. Save group and count it. - databaseToOpen.addEntryIndex(newEnt); - newEnt = databaseToOpen.createEntry(); - i++; - } - else { - readEntryField(databaseToOpen, newEnt, filebuf, pos); - } - pos += 2 + 4 + fieldSize; - } - - databaseToOpen.constructTreeFromIndex(); - - return databaseToOpen; - } - - /** - * Parse and save one record from binary file. - * @param buf - * @param offset - * @return If >0, - * @throws UnsupportedEncodingException - */ - private void readGroupField(PwDatabaseV3 db, PwGroupV3 grp, int fieldType, byte[] buf, int offset) throws UnsupportedEncodingException { - switch( fieldType ) { - case 0x0000 : - // Ignore field - break; - case 0x0001 : - grp.setGroupId(LEDataInputStream.readInt(buf, offset)); - break; - case 0x0002 : - grp.setTitle(Types.readCString(buf, offset)); - break; - case 0x0003 : - grp.setCreationTime(new PwDate(buf, offset)); - break; - case 0x0004 : - grp.setLastModificationTime(new PwDate(buf, offset)); - break; - case 0x0005 : - grp.setLastAccessTime(new PwDate(buf, offset)); - break; - case 0x0006 : - grp.setExpiryTime(new PwDate(buf, offset)); - break; - case 0x0007 : - grp.setIcon(db.getIconFactory().getIcon(LEDataInputStream.readInt(buf, offset))); - break; - case 0x0008 : - grp.setLevel(LEDataInputStream.readUShort(buf, offset)); - break; - case 0x0009 : - grp.setFlags(LEDataInputStream.readInt(buf, offset)); - break; - } - } - - private void readEntryField(PwDatabaseV3 db, PwEntryV3 ent, byte[] buf, int offset) throws UnsupportedEncodingException { - int fieldType = LEDataInputStream.readUShort(buf, offset); - offset += 2; - int fieldSize = LEDataInputStream.readInt(buf, offset); - offset += 4; - - switch( fieldType ) { - case 0x0000 : - // Ignore field - break; - case 0x0001 : - ent.setNodeId(new PwNodeIdUUID(Types.bytestoUUID(buf, offset))); - break; - case 0x0002 : - PwGroupV3 pwGroupV3 = databaseToOpen.createGroup(); - pwGroupV3.setNodeId(new PwNodeIdInt(LEDataInputStream.readInt(buf, offset))); - ent.setParent(pwGroupV3); - break; - case 0x0003 : - int iconId = LEDataInputStream.readInt(buf, offset); - - // Clean up after bug that set icon ids to -1 - if (iconId == -1) { - iconId = 0; - } - - ent.setIcon(db.getIconFactory().getIcon(iconId)); - break; - case 0x0004 : - ent.setTitle(Types.readCString(buf, offset)); - break; - case 0x0005 : - ent.setUrl(Types.readCString(buf, offset)); - break; - case 0x0006 : - ent.setUsername(Types.readCString(buf, offset)); - break; - case 0x0007 : - ent.setPassword(buf, offset, Types.strlen(buf, offset)); - break; - case 0x0008 : - ent.setNotes(Types.readCString(buf, offset)); - break; - case 0x0009 : - ent.setCreationTime(new PwDate(buf, offset)); - break; - case 0x000A : - ent.setLastModificationTime(new PwDate(buf, offset)); - break; - case 0x000B : - ent.setLastAccessTime(new PwDate(buf, offset)); - break; - case 0x000C : - ent.setExpiryTime(new PwDate(buf, offset)); - break; - case 0x000D : - ent.setBinaryDesc(Types.readCString(buf, offset)); - break; - case 0x000E : - ent.setBinaryData(buf, offset, fieldSize); - break; - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.kt new file mode 100644 index 000000000..074bc88e7 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.kt @@ -0,0 +1,297 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + * + +Derived from + +KeePass for J2ME + +Copyright 2007 Naomaru Itoi + +This file was derived from + +Java clone of KeePass - A KeePass file viewer for Java +Copyright 2006 Bill Zwicky + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.kunzisoft.keepass.database.load + +import android.util.Log +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.crypto.CipherFactory +import com.kunzisoft.keepass.database.element.* +import com.kunzisoft.keepass.database.exception.* +import com.kunzisoft.keepass.stream.LEDataInputStream +import com.kunzisoft.keepass.stream.NullOutputStream +import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import com.kunzisoft.keepass.utils.Types + +import javax.crypto.* +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec +import java.io.IOException +import java.io.InputStream +import java.io.UnsupportedEncodingException +import java.security.* +import java.util.Arrays + +/** + * Load a v3 database file. + * + * @author Naomaru Itoi @phoneid.org> + * @author Bill Zwicky @pobox.com> + */ +class ImporterV3 : Importer() { + + private lateinit var mDatabaseToOpen: PwDatabaseV3 + + @Throws(IOException::class, InvalidDBException::class) + override fun openDatabase(databaseInputStream: InputStream, + password: String?, + keyInputStream: InputStream?, + progressTaskUpdater: ProgressTaskUpdater?): PwDatabaseV3 { + + // Load entire file, most of it's encrypted. + val fileSize = databaseInputStream.available() + val filebuf = ByteArray(fileSize + 16) // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer + databaseInputStream.read(filebuf, 0, fileSize) // TODO remove + databaseInputStream.close() + + // Parse header (unencrypted) + if (fileSize < PwDbHeaderV3.BUF_SIZE) + throw IOException("File too short for header") + val hdr = PwDbHeaderV3() + hdr.loadFromFile(filebuf, 0) + + if (hdr.signature1 != PwDbHeader.PWM_DBSIG_1 || hdr.signature2 != PwDbHeaderV3.DBSIG_2) { + throw InvalidDBSignatureException() + } + + if (!hdr.matchesVersion()) { + throw InvalidDBVersionException() + } + + progressTaskUpdater?.updateMessage(R.string.retrieving_db_key) + mDatabaseToOpen = PwDatabaseV3() + mDatabaseToOpen.retrieveMasterKey(password, keyInputStream) + + // Select algorithm + if (hdr.flags and PwDbHeaderV3.FLAG_RIJNDAEL != 0) { + mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.AESRijndael + } else if (hdr.flags and PwDbHeaderV3.FLAG_TWOFISH != 0) { + mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.Twofish + } else { + throw InvalidAlgorithmException() + } + + mDatabaseToOpen.numberKeyEncryptionRounds = hdr.numKeyEncRounds.toLong() + + // Generate transformedMasterKey from masterKey + mDatabaseToOpen.makeFinalKey(hdr.masterSeed, hdr.transformSeed, mDatabaseToOpen.numberKeyEncryptionRounds) + + progressTaskUpdater?.updateMessage(R.string.decrypting_db) + // Initialize Rijndael algorithm + val cipher: Cipher + try { + if (mDatabaseToOpen.encryptionAlgorithm === PwEncryptionAlgorithm.AESRijndael) { + cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding") + } else if (mDatabaseToOpen.encryptionAlgorithm === PwEncryptionAlgorithm.Twofish) { + cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING") + } else { + throw IOException("Encryption algorithm is not supported") + } + + } catch (e1: NoSuchAlgorithmException) { + throw IOException("No such algorithm") + } catch (e1: NoSuchPaddingException) { + throw IOException("No such pdading") + } + + try { + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(mDatabaseToOpen.finalKey, "AES"), IvParameterSpec(hdr.encryptionIV)) + } catch (e1: InvalidKeyException) { + throw IOException("Invalid key") + } catch (e1: InvalidAlgorithmParameterException) { + throw IOException("Invalid algorithm parameter.") + } + + // Decrypt! The first bytes aren't encrypted (that's the header) + val encryptedPartSize: Int + try { + encryptedPartSize = cipher.doFinal(filebuf, PwDbHeaderV3.BUF_SIZE, fileSize - PwDbHeaderV3.BUF_SIZE, filebuf, PwDbHeaderV3.BUF_SIZE) + } catch (e1: ShortBufferException) { + throw IOException("Buffer too short") + } catch (e1: IllegalBlockSizeException) { + throw IOException("Invalid block size") + } catch (e1: BadPaddingException) { + throw InvalidPasswordException() + } + + var md: MessageDigest? = null + try { + md = MessageDigest.getInstance("SHA-256") + } catch (e: NoSuchAlgorithmException) { + throw IOException("No SHA-256 algorithm") + } + + val nos = NullOutputStream() + val dos = DigestOutputStream(nos, md) + dos.write(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize) + dos.close() + val hash = md!!.digest() + + if (!Arrays.equals(hash, hdr.contentsHash)) { + + Log.w(TAG, "Database file did not decrypt correctly. (checksum code is broken)") + throw InvalidPasswordException() + } + + // New manual root because V3 contains multiple root groups (here available with getRootGroups()) + val newRoot = mDatabaseToOpen.createGroup() + newRoot.level = -1 + mDatabaseToOpen.rootGroup = newRoot + + // Import all groups + var pos = PwDbHeaderV3.BUF_SIZE + var newGrp = mDatabaseToOpen.createGroup() + run { + var i = 0 + while (i < hdr.numGroups) { + val fieldType = LEDataInputStream.readUShort(filebuf, pos) + pos += 2 + val fieldSize = LEDataInputStream.readInt(filebuf, pos) + pos += 4 + + if (fieldType == 0xFFFF) { + // End-Group record. Save group and count it. + mDatabaseToOpen.addGroupIndex(newGrp) + newGrp = mDatabaseToOpen.createGroup() + i++ + } else { + readGroupField(mDatabaseToOpen, newGrp, fieldType, filebuf, pos) + } + pos += fieldSize + } + } + + // Import all entries + var newEnt = mDatabaseToOpen.createEntry() + var i = 0 + while (i < hdr.numEntries) { + val fieldType = LEDataInputStream.readUShort(filebuf, pos) + val fieldSize = LEDataInputStream.readInt(filebuf, pos + 2) + + if (fieldType == 0xFFFF) { + // End-Group record. Save group and count it. + mDatabaseToOpen.addEntryIndex(newEnt) + newEnt = mDatabaseToOpen.createEntry() + i++ + } else { + readEntryField(mDatabaseToOpen, newEnt, filebuf, pos) + } + pos += 2 + 4 + fieldSize + } + + mDatabaseToOpen.constructTreeFromIndex() + + return mDatabaseToOpen + } + + /** + * Parse and save one record from binary file. + * @param buf + * @param offset + * @return If >0, + * @throws UnsupportedEncodingException + */ + @Throws(UnsupportedEncodingException::class) + private fun readGroupField(db: PwDatabaseV3, grp: PwGroupV3, fieldType: Int, buf: ByteArray, offset: Int) { + when (fieldType) { + 0x0000 -> { + } + 0x0001 -> grp.setGroupId(LEDataInputStream.readInt(buf, offset)) + 0x0002 -> grp.title = Types.readCString(buf, offset) + 0x0003 -> grp.creationTime = PwDate(buf, offset) + 0x0004 -> grp.lastModificationTime = PwDate(buf, offset) + 0x0005 -> grp.lastAccessTime = PwDate(buf, offset) + 0x0006 -> grp.expiryTime = PwDate(buf, offset) + 0x0007 -> grp.icon = db.iconFactory.getIcon(LEDataInputStream.readInt(buf, offset)) + 0x0008 -> grp.level = LEDataInputStream.readUShort(buf, offset) + 0x0009 -> grp.flags = LEDataInputStream.readInt(buf, offset) + }// Ignore field + } + + @Throws(UnsupportedEncodingException::class) + private fun readEntryField(db: PwDatabaseV3, ent: PwEntryV3, buf: ByteArray, offset: Int) { + var offsetMutable = offset + val fieldType = LEDataInputStream.readUShort(buf, offsetMutable) + offsetMutable += 2 + val fieldSize = LEDataInputStream.readInt(buf, offsetMutable) + offsetMutable += 4 + + when (fieldType) { + 0x0000 -> { + } + 0x0001 -> ent.nodeId = PwNodeIdUUID(Types.bytestoUUID(buf, offsetMutable)) + 0x0002 -> { + val pwGroupV3 = mDatabaseToOpen.createGroup() + pwGroupV3.nodeId = PwNodeIdInt(LEDataInputStream.readInt(buf, offsetMutable)) + ent.parent = pwGroupV3 + } + 0x0003 -> { + var iconId = LEDataInputStream.readInt(buf, offsetMutable) + + // Clean up after bug that set icon ids to -1 + if (iconId == -1) { + iconId = 0 + } + + ent.icon = db.iconFactory.getIcon(iconId) + } + 0x0004 -> ent.title = Types.readCString(buf, offsetMutable) + 0x0005 -> ent.url = Types.readCString(buf, offsetMutable) + 0x0006 -> ent.username = Types.readCString(buf, offsetMutable) + 0x0007 -> ent.setPassword(buf, offsetMutable, Types.strlen(buf, offsetMutable)) + 0x0008 -> ent.notes = Types.readCString(buf, offsetMutable) + 0x0009 -> ent.creationTime = PwDate(buf, offsetMutable) + 0x000A -> ent.lastModificationTime = PwDate(buf, offsetMutable) + 0x000B -> ent.lastAccessTime = PwDate(buf, offsetMutable) + 0x000C -> ent.expiryTime = PwDate(buf, offsetMutable) + 0x000D -> ent.binaryDesc = Types.readCString(buf, offsetMutable) + 0x000E -> ent.setBinaryData(buf, offsetMutable, fieldSize) + }// Ignore field + } + + companion object { + private val TAG = ImporterV3::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java deleted file mode 100644 index b865feace..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.java +++ /dev/null @@ -1,1139 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.load; - -import android.support.annotation.NonNull; - -import biz.source_code.base64Coder.Base64Coder; -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.crypto.PwStreamCipherFactory; -import com.kunzisoft.keepass.crypto.engine.CipherEngine; -import com.kunzisoft.keepass.database.element.NodeV4Interface; -import com.kunzisoft.keepass.database.PwCompressionAlgorithm; -import com.kunzisoft.keepass.database.element.*; -import com.kunzisoft.keepass.database.exception.ArcFourException; -import com.kunzisoft.keepass.database.exception.InvalidDBException; -import com.kunzisoft.keepass.database.exception.InvalidPasswordException; -import com.kunzisoft.keepass.database.security.ProtectedBinary; -import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.stream.BetterCipherInputStream; -import com.kunzisoft.keepass.stream.HashedBlockInputStream; -import com.kunzisoft.keepass.stream.HmacBlockInputStream; -import com.kunzisoft.keepass.stream.LEDataInputStream; -import com.kunzisoft.keepass.tasks.ProgressTaskUpdater; -import com.kunzisoft.keepass.utils.DateUtil; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.MemUtil; -import com.kunzisoft.keepass.utils.Types; -import org.spongycastle.crypto.StreamCipher; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; -import java.io.*; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.text.ParseException; -import java.util.Arrays; -import java.util.Date; -import java.util.Stack; -import java.util.UUID; -import java.util.zip.GZIPInputStream; - -public class ImporterV4 extends Importer { - - private StreamCipher randomStream; - private PwDatabaseV4 mDatabase; - - private byte[] hashOfHeader = null; - private long version; - private File streamDir; - - public ImporterV4(File streamDir) { - this.streamDir = streamDir; - } - - @NonNull - @Override - public PwDatabaseV4 openDatabase(@NonNull InputStream inStream, - String password, - InputStream keyInputStream, - ProgressTaskUpdater progressTaskUpdater) - throws IOException, InvalidDBException { - - if (progressTaskUpdater != null) - progressTaskUpdater.updateMessage(R.string.retrieving_db_key); - mDatabase = new PwDatabaseV4(); - - PwDbHeaderV4 header = new PwDbHeaderV4(mDatabase); - mDatabase.getBinPool().clear(); - - PwDbHeaderV4.HeaderAndHash hh = header.loadFromFile(inStream); - version = header.getVersion(); - - hashOfHeader = hh.hash; - byte[] pbHeader = hh.header; - - mDatabase.retrieveMasterKey(password, keyInputStream); - mDatabase.makeFinalKey(header.getMasterSeed()); - - if (progressTaskUpdater != null) - progressTaskUpdater.updateMessage(R.string.decrypting_db); - CipherEngine engine; - Cipher cipher; - try { - engine = CipherFactory.getInstance(mDatabase.getDataCipher()); - mDatabase.setDataEngine(engine); - mDatabase.setEncryptionAlgorithm(engine.getPwEncryptionAlgorithm()); - cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.getFinalKey(), header.getEncryptionIV()); - } catch (NoSuchAlgorithmException|NoSuchPaddingException|InvalidKeyException|InvalidAlgorithmParameterException e) { - throw new IOException("Invalid algorithm.", e); - } - - InputStream isPlain; - if (version < PwDbHeaderV4.FILE_VERSION_32_4) { - - InputStream decrypted = AttachCipherStream(inStream, cipher); - LEDataInputStream dataDecrypted = new LEDataInputStream(decrypted); - byte[] storedStartBytes = null; - try { - storedStartBytes = dataDecrypted.readBytes(32); - if (storedStartBytes == null || storedStartBytes.length != 32) { - throw new InvalidPasswordException(); - } - } catch (IOException e) { - throw new InvalidPasswordException(); - } - - if (!Arrays.equals(storedStartBytes, header.streamStartBytes)) { - throw new InvalidPasswordException(); - } - - isPlain = new HashedBlockInputStream(dataDecrypted); - } - else { // KDBX 4 - LEDataInputStream isData = new LEDataInputStream(inStream); - byte[] storedHash = isData.readBytes(32); - if (!Arrays.equals(storedHash,hashOfHeader)) { - throw new InvalidDBException(); - } - - byte[] hmacKey = mDatabase.getHmacKey(); - byte[] headerHmac = PwDbHeaderV4.computeHeaderHmac(pbHeader, hmacKey); - byte[] storedHmac = isData.readBytes(32); - if (storedHmac == null || storedHmac.length != 32) { - throw new InvalidDBException(); - } - // Mac doesn't match - if (! Arrays.equals(headerHmac, storedHmac)) { - throw new InvalidDBException(); - } - - HmacBlockInputStream hmIs = new HmacBlockInputStream(isData, true, hmacKey); - - isPlain = AttachCipherStream(hmIs, cipher); - } - - InputStream isXml; - if ( mDatabase.getCompressionAlgorithm() == PwCompressionAlgorithm.Gzip ) { - isXml = new GZIPInputStream(isPlain); - } else { - isXml = isPlain; - } - - if (version >= PwDbHeaderV4.FILE_VERSION_32_4) { - LoadInnerHeader(isXml, header); - } - - if ( header.innerRandomStreamKey == null ) { - throw new IOException("Invalid stream key."); - } - - randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey); - - if ( randomStream == null ) { - throw new ArcFourException(); - } - - ReadXmlStreamed(isXml); - - return mDatabase; - } - - private InputStream AttachCipherStream(InputStream is, Cipher cipher) { - return new BetterCipherInputStream(is, cipher, 50 * 1024); - } - - private void LoadInnerHeader(InputStream is, PwDbHeaderV4 header) throws IOException { - LEDataInputStream lis = new LEDataInputStream(is); - - while(true) { - if (!ReadInnerHeader(lis, header)) break; - } - } - - private String getUnusedCacheFileName() { - return String.valueOf(mDatabase.getBinPool().findUnusedKey()); - } - - private boolean ReadInnerHeader(LEDataInputStream lis, PwDbHeaderV4 header) throws IOException { - byte fieldId = (byte)lis.read(); - - int size = lis.readInt(); - if (size < 0) throw new IOException("Corrupted file"); - - byte[] data = new byte[0]; - if (size > 0) { - if (fieldId != PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary) - data = lis.readBytes(size); - } - - boolean result = true; - switch(fieldId) { - case PwDbHeaderV4.PwDbInnerHeaderV4Fields.EndOfHeader: - result = false; - break; - case PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID: - header.setRandomStreamID(data); - break; - case PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey: - header.innerRandomStreamKey = data; - break; - case PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary: - byte flag = lis.readBytes(1)[0]; - boolean protectedFlag = (flag & PwDbHeaderV4.KdbxBinaryFlags.Protected) != - PwDbHeaderV4.KdbxBinaryFlags.None; - int byteLength = size - 1; - // Read in a file - File file = new File(streamDir, getUnusedCacheFileName()); - try (FileOutputStream outputStream = new FileOutputStream(file)) { - lis.readBytes(byteLength, outputStream::write); - } - ProtectedBinary protectedBinary = new ProtectedBinary(protectedFlag, file, byteLength); - mDatabase.getBinPool().add(protectedBinary); - break; - - default: - break; - } - - return result; - } - - private enum KdbContext { - Null, - KeePassFile, - Meta, - Root, - MemoryProtection, - CustomIcons, - CustomIcon, - CustomData, - CustomDataItem, - RootDeletedObjects, - DeletedObject, - Group, - GroupTimes, - GroupCustomData, - GroupCustomDataItem, - Entry, - EntryTimes, - EntryString, - EntryBinary, - EntryAutoType, - EntryAutoTypeItem, - EntryHistory, - EntryCustomData, - EntryCustomDataItem, - Binaries - } - - private static final long DEFAULT_HISTORY_DAYS = 365; - - private boolean readNextNode = true; - private Stack ctxGroups = new Stack<>(); - private PwGroupV4 ctxGroup = null; - private PwEntryV4 ctxEntry = null; - private String ctxStringName = null; - private ProtectedString ctxStringValue = null; - private String ctxBinaryName = null; - private ProtectedBinary ctxBinaryValue = null; - private String ctxATName = null; - private String ctxATSeq = null; - private boolean entryInHistory = false; - private PwEntryV4 ctxHistoryBase = null; - private PwDeletedObject ctxDeletedObject = null; - private UUID customIconID = PwDatabase.UUID_ZERO; - private byte[] customIconData; - private String customDataKey = null; - private String customDataValue = null; - private String groupCustomDataKey = null; - private String groupCustomDataValue = null; - private String entryCustomDataKey = null; - private String entryCustomDataValue = null; - - private void ReadXmlStreamed(InputStream readerStream) throws IOException, InvalidDBException { - try { - ReadDocumentStreamed(CreatePullParser(readerStream)); - } catch (XmlPullParserException e) { - e.printStackTrace(); - throw new IOException(e.getLocalizedMessage()); - } - } - - private static XmlPullParser CreatePullParser(InputStream readerStream) throws XmlPullParserException { - XmlPullParserFactory xppf = XmlPullParserFactory.newInstance(); - xppf.setNamespaceAware(false); - - XmlPullParser xpp = xppf.newPullParser(); - xpp.setInput(readerStream, null); - - return xpp; - } - - private void ReadDocumentStreamed(XmlPullParser xpp) throws XmlPullParserException, IOException, InvalidDBException { - - ctxGroups.clear(); - - KdbContext ctx = KdbContext.Null; - - readNextNode = true; - - while (true) { - if ( readNextNode ) { - if( xpp.next() == XmlPullParser.END_DOCUMENT ) break; - } else { - readNextNode = true; - } - - switch ( xpp.getEventType() ) { - case XmlPullParser.START_TAG: - ctx = ReadXmlElement(ctx, xpp); - break; - - case XmlPullParser.END_TAG: - ctx = EndXmlElement(ctx, xpp); - break; - - default: - break; - } - } - - // Error checks - if ( ctx != KdbContext.Null ) throw new IOException("Malformed"); - if ( ctxGroups.size() != 0 ) throw new IOException("Malformed"); - } - - - private KdbContext ReadXmlElement(KdbContext ctx, XmlPullParser xpp) throws XmlPullParserException, IOException, InvalidDBException { - String name = xpp.getName(); - switch (ctx) { - case Null: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDocNode) ) { - return SwitchContext(ctx, KdbContext.KeePassFile, xpp); - } else ReadUnknown(xpp); - break; - - case KeePassFile: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemMeta) ) { - return SwitchContext(ctx, KdbContext.Meta, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemRoot) ) { - return SwitchContext(ctx, KdbContext.Root, xpp); - } else { - ReadUnknown(xpp); - } - break; - - case Meta: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemGenerator) ) { - ReadString(xpp); // Ignore - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemHeaderHash) ) { - String encodedHash = ReadString(xpp); - if (!EmptyUtils.isNullOrEmpty(encodedHash) && (hashOfHeader != null)) { - byte[] hash = Base64Coder.decode(encodedHash); - if (!Arrays.equals(hash, hashOfHeader)) { - throw new InvalidDBException(); - } - } - } else if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemSettingsChanged)) { - mDatabase.setSettingsChanged(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbName) ) { - mDatabase.setName(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbNameChanged) ) { - mDatabase.setNameChanged(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDesc) ) { - mDatabase.setDescription(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDescChanged) ) { - mDatabase.setDescriptionChanged(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDefaultUser) ) { - mDatabase.setDefaultUserName(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbDefaultUserChanged) ) { - mDatabase.setDefaultUserNameChanged(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbColor)) { - // TODO: Add support to interpret the color if we want to allow changing the database color - mDatabase.setColor(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbMntncHistoryDays) ) { - mDatabase.setMaintenanceHistoryDays(ReadUInt(xpp, DEFAULT_HISTORY_DAYS)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChanged) ) { - mDatabase.setKeyLastChanged(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChangeRec) ) { - mDatabase.setKeyChangeRecDays(ReadLong(xpp, -1)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChangeForce) ) { - mDatabase.setKeyChangeForceDays(ReadLong(xpp, -1)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDbKeyChangeForceOnce) ) { - mDatabase.setKeyChangeForceOnce(ReadBool(xpp, false)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemMemoryProt) ) { - return SwitchContext(ctx, KdbContext.MemoryProtection, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIcons) ) { - return SwitchContext(ctx, KdbContext.CustomIcons, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemRecycleBinEnabled) ) { - mDatabase.setRecycleBinEnabled(ReadBool(xpp, true)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemRecycleBinUuid) ) { - mDatabase.setRecycleBinUUID(ReadUuid(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemRecycleBinChanged) ) { - mDatabase.setRecycleBinChanged(ReadTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntryTemplatesGroup) ) { - mDatabase.setEntryTemplatesGroup(ReadUuid(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntryTemplatesGroupChanged) ) { - mDatabase.setEntryTemplatesGroupChanged(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemHistoryMaxItems) ) { - mDatabase.setHistoryMaxItems(ReadInt(xpp, -1)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemHistoryMaxSize) ) { - mDatabase.setHistoryMaxSize(ReadLong(xpp, -1)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLastSelectedGroup) ) { - mDatabase.setLastSelectedGroup(ReadUuid(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLastTopVisibleGroup) ) { - mDatabase.setLastTopVisibleGroup(ReadUuid(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemBinaries) ) { - return SwitchContext(ctx, KdbContext.Binaries, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData) ) { - return SwitchContext(ctx, KdbContext.CustomData, xpp); - } - break; - - case MemoryProtection: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtTitle) ) { - mDatabase.getMemoryProtection().setProtectTitle(ReadBool(xpp, false)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtUserName) ) { - mDatabase.getMemoryProtection().setProtectUserName(ReadBool(xpp, false)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtPassword) ) { - mDatabase.getMemoryProtection().setProtectPassword(ReadBool(xpp, false)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtURL) ) { - mDatabase.getMemoryProtection().setProtectUrl(ReadBool(xpp, false)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtNotes) ) { - mDatabase.getMemoryProtection().setProtectNotes(ReadBool(xpp, false)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemProtAutoHide) ) { - mDatabase.getMemoryProtection().setAutoEnableVisualHiding(ReadBool(xpp, false)); - } else { - ReadUnknown(xpp); - } - break; - - case CustomIcons: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconItem) ) { - return SwitchContext(ctx, KdbContext.CustomIcon, xpp); - } else { - ReadUnknown(xpp); - } - break; - - case CustomIcon: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconItemID) ) { - customIconID = ReadUuid(xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconItemData) ) { - String strData = ReadString(xpp); - if ( strData != null && strData.length() > 0 ) { - customIconData = Base64Coder.decode(strData); - } else { - assert(false); - } - } else { - ReadUnknown(xpp); - } - break; - - case Binaries: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemBinary) ) { - String key = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrId); - if ( key != null ) { - ProtectedBinary pbData = ReadProtectedBinary(xpp); - int id = Integer.parseInt(key); - mDatabase.getBinPool().put(id, pbData); - } else { - ReadUnknown(xpp); - } - } else { - ReadUnknown(xpp); - } - - break; - - case CustomData: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemStringDictExItem) ) { - return SwitchContext(ctx, KdbContext.CustomDataItem, xpp); - } else { - ReadUnknown(xpp); - } - break; - - case CustomDataItem: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemKey) ) { - customDataKey = ReadString(xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemValue) ) { - customDataValue = ReadString(xpp); - } else { - ReadUnknown(xpp); - } - break; - - case Root: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - if ( ctxGroups.size() != 0 ) - throw new IOException("Group list should be empty."); - - mDatabase.setRootGroup(mDatabase.createGroup()); - ctxGroups.push(mDatabase.getRootGroup()); - ctxGroup = ctxGroups.peek(); - - return SwitchContext(ctx, KdbContext.Group, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDeletedObjects) ) { - return SwitchContext(ctx, KdbContext.RootDeletedObjects, xpp); - } else { - ReadUnknown(xpp); - } - break; - - case Group: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { - ctxGroup.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); - mDatabase.addGroupIndex(ctxGroup); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemName) ) { - ctxGroup.setTitle(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemNotes) ) { - ctxGroup.setNotes(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { - ctxGroup.setIcon(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { - ctxGroup.setIconCustom(mDatabase.getIconFactory().getIcon(ReadUuid(xpp))); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemTimes) ) { - return SwitchContext(ctx, KdbContext.GroupTimes, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIsExpanded) ) { - ctxGroup.setExpanded(ReadBool(xpp, true)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroupDefaultAutoTypeSeq) ) { - ctxGroup.setDefaultAutoTypeSequence(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEnableAutoType) ) { - ctxGroup.setEnableAutoType(StringToBoolean(ReadString(xpp))); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEnableSearching) ) { - ctxGroup.setEnableSearching(StringToBoolean(ReadString(xpp))); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLastTopVisibleEntry) ) { - ctxGroup.setLastTopVisibleEntry(ReadUuid(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData) ) { - return SwitchContext(ctx, KdbContext.GroupCustomData, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - ctxGroup = mDatabase.createGroup(); - PwGroupV4 groupPeek = ctxGroups.peek(); - groupPeek.addChildGroup(ctxGroup); - ctxGroup.setParent(groupPeek); - ctxGroups.push(ctxGroup); - - return SwitchContext(ctx, KdbContext.Group, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { - ctxEntry = mDatabase.createEntry(); - ctxGroup.addChildEntry(ctxEntry); - ctxEntry.setParent(ctxGroup); - - entryInHistory = false; - return SwitchContext(ctx, KdbContext.Entry, xpp); - } else { - ReadUnknown(xpp); - } - break; - case GroupCustomData: - if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemStringDictExItem)) { - return SwitchContext(ctx, KdbContext.GroupCustomDataItem, xpp); - } else { - ReadUnknown(xpp); - } - break; - case GroupCustomDataItem: - if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemKey)) { - groupCustomDataKey = ReadString(xpp); - } else if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemValue)) { - groupCustomDataValue = ReadString(xpp); - } else { - ReadUnknown(xpp); - } - break; - - - case Entry: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { - ctxEntry.setNodeId(new PwNodeIdUUID(ReadUuid(xpp))); - mDatabase.addEntryIndex(ctxEntry); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemIcon) ) { - ctxEntry.setIcon(mDatabase.getIconFactory().getIcon((int)ReadUInt(xpp, 0))); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconID) ) { - ctxEntry.setIconCustom(mDatabase.getIconFactory().getIcon(ReadUuid(xpp))); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemFgColor) ) { - ctxEntry.setForegroundColor(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemBgColor) ) { - ctxEntry.setBackgroundColor(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemOverrideUrl) ) { - ctxEntry.setOverrideURL(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemTags) ) { - ctxEntry.setTags(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemTimes) ) { - return SwitchContext(ctx, KdbContext.EntryTimes, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemString) ) { - return SwitchContext(ctx, KdbContext.EntryString, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemBinary) ) { - return SwitchContext(ctx, KdbContext.EntryBinary, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoType) ) { - return SwitchContext(ctx, KdbContext.EntryAutoType, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData)) { - return SwitchContext(ctx, KdbContext.EntryCustomData, xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemHistory) ) { - if ( ! entryInHistory ) { - ctxHistoryBase = ctxEntry; - return SwitchContext(ctx, KdbContext.EntryHistory, xpp); - } else { - ReadUnknown(xpp); - } - } else { - ReadUnknown(xpp); - } - break; - case EntryCustomData: - if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemStringDictExItem)) { - return SwitchContext(ctx, KdbContext.EntryCustomDataItem, xpp); - } else { - ReadUnknown(xpp); - } - break; - case EntryCustomDataItem: - if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemKey)) { - entryCustomDataKey = ReadString(xpp); - } else if (name.equalsIgnoreCase(PwDatabaseV4XML.ElemValue)) { - entryCustomDataValue = ReadString(xpp); - } else { - ReadUnknown(xpp); - } - break; - - case GroupTimes: - case EntryTimes: - NodeV4Interface tl; - if ( ctx == KdbContext.GroupTimes ) { - tl = ctxGroup; - } else { - tl = ctxEntry; - } - - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLastModTime) ) { - tl.setLastModificationTime(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemCreationTime) ) { - tl.setCreationTime(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLastAccessTime) ) { - tl.setLastAccessTime(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemExpiryTime) ) { - tl.setExpiryTime(ReadPwTime(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemExpires) ) { - tl.setExpires(ReadBool(xpp, false)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUsageCount) ) { - tl.setUsageCount(ReadULong(xpp, 0)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemLocationChanged) ) { - tl.setLocationChanged(ReadPwTime(xpp)); - } else { - ReadUnknown(xpp); - } - break; - - case EntryString: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemKey) ) { - ctxStringName = ReadString(xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemValue) ) { - ctxStringValue = ReadProtectedString(xpp); - } else { - ReadUnknown(xpp); - } - break; - - case EntryBinary: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemKey) ) { - ctxBinaryName = ReadString(xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemValue) ) { - ctxBinaryValue = ReadProtectedBinary(xpp); - } - break; - - case EntryAutoType: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeEnabled) ) { - ctxEntry.getAutoType().setEnabled(ReadBool(xpp, true)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeObfuscation) ) { - ctxEntry.getAutoType().setObfuscationOptions(ReadUInt(xpp, 0)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeDefaultSeq) ) { - ctxEntry.getAutoType().setDefaultSequence(ReadString(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeItem) ) { - return SwitchContext(ctx, KdbContext.EntryAutoTypeItem, xpp); - } else { - ReadUnknown(xpp); - } - break; - - case EntryAutoTypeItem: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemWindow) ) { - ctxATName = ReadString(xpp); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemKeystrokeSequence) ) { - ctxATSeq = ReadString(xpp); - } else { - ReadUnknown(xpp); - } - break; - - case EntryHistory: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { - ctxEntry = new PwEntryV4(); - ctxHistoryBase.addToHistory(ctxEntry); - - entryInHistory = true; - return SwitchContext(ctx, KdbContext.Entry, xpp); - } else { - ReadUnknown(xpp); - } - break; - - case RootDeletedObjects: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDeletedObject) ) { - ctxDeletedObject = new PwDeletedObject(); - mDatabase.addDeletedObject(ctxDeletedObject); - - return SwitchContext(ctx, KdbContext.DeletedObject, xpp); - } else { - ReadUnknown(xpp); - } - break; - - case DeletedObject: - if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemUuid) ) { - ctxDeletedObject.setUuid(ReadUuid(xpp)); - } else if ( name.equalsIgnoreCase(PwDatabaseV4XML.ElemDeletionTime) ) { - ctxDeletedObject.setDeletionTime(ReadTime(xpp)); - } else { - ReadUnknown(xpp); - } - break; - - default: - ReadUnknown(xpp); - break; - } - - return ctx; - } - - private KdbContext EndXmlElement(KdbContext ctx, XmlPullParser xpp) throws XmlPullParserException { - // (xpp.getEventType() == XmlPullParser.END_TAG); - - String name = xpp.getName(); - if ( ctx == KdbContext.KeePassFile && name.equalsIgnoreCase(PwDatabaseV4XML.ElemDocNode) ) { - return KdbContext.Null; - } else if ( ctx == KdbContext.Meta && name.equalsIgnoreCase(PwDatabaseV4XML.ElemMeta) ) { - return KdbContext.KeePassFile; - } else if ( ctx == KdbContext.Root && name.equalsIgnoreCase(PwDatabaseV4XML.ElemRoot) ) { - return KdbContext.KeePassFile; - } else if ( ctx == KdbContext.MemoryProtection && name.equalsIgnoreCase(PwDatabaseV4XML.ElemMemoryProt) ) { - return KdbContext.Meta; - } else if ( ctx == KdbContext.CustomIcons && name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIcons) ) { - return KdbContext.Meta; - } else if ( ctx == KdbContext.CustomIcon && name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomIconItem) ) { - if ( ! customIconID.equals(PwDatabase.UUID_ZERO) ) { - PwIconCustom icon = new PwIconCustom(customIconID, customIconData); - mDatabase.addCustomIcon(icon); - mDatabase.getIconFactory().put(icon); - } - - customIconID = PwDatabase.UUID_ZERO; - customIconData = null; - - return KdbContext.CustomIcons; - } else if ( ctx == KdbContext.Binaries && name.equalsIgnoreCase(PwDatabaseV4XML.ElemBinaries) ) { - return KdbContext.Meta; - } else if ( ctx == KdbContext.CustomData && name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData) ) { - return KdbContext.Meta; - } else if ( ctx == KdbContext.CustomDataItem && name.equalsIgnoreCase(PwDatabaseV4XML.ElemStringDictExItem) ) { - if ( customDataKey != null && customDataValue != null) { - mDatabase.putCustomData(customDataKey, customDataValue); - } - - customDataKey = null; - customDataValue = null; - - return KdbContext.CustomData; - } else if ( ctx == KdbContext.Group && name.equalsIgnoreCase(PwDatabaseV4XML.ElemGroup) ) { - if ( ctxGroup.getId().equals(PwDatabase.UUID_ZERO) ) { - ctxGroup.setNodeId(mDatabase.newGroupId()); - mDatabase.addGroupIndex(ctxGroup); - } - - ctxGroups.pop(); - - if ( ctxGroups.size() == 0 ) { - ctxGroup = null; - return KdbContext.Root; - } else { - ctxGroup = ctxGroups.peek(); - return KdbContext.Group; - } - } else if ( ctx == KdbContext.GroupTimes && name.equalsIgnoreCase(PwDatabaseV4XML.ElemTimes) ) { - return KdbContext.Group; - } else if ( ctx == KdbContext.GroupCustomData && name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData) ) { - return KdbContext.Group; - } else if ( ctx == KdbContext.GroupCustomDataItem && name.equalsIgnoreCase(PwDatabaseV4XML.ElemStringDictExItem)) { - if (groupCustomDataKey != null && groupCustomDataValue != null) { - ctxGroup.putCustomData(groupCustomDataKey, groupCustomDataKey); - } - - groupCustomDataKey = null; - groupCustomDataValue = null; - - return KdbContext.GroupCustomData; - - } else if ( ctx == KdbContext.Entry && name.equalsIgnoreCase(PwDatabaseV4XML.ElemEntry) ) { - if ( ctxEntry.getId().equals(PwDatabase.UUID_ZERO) ) { - ctxEntry.setNodeId(mDatabase.newEntryId()); - mDatabase.addEntryIndex(ctxEntry); - } - - if ( entryInHistory ) { - ctxEntry = ctxHistoryBase; - return KdbContext.EntryHistory; - } - - return KdbContext.Group; - } else if ( ctx == KdbContext.EntryTimes && name.equalsIgnoreCase(PwDatabaseV4XML.ElemTimes) ) { - return KdbContext.Entry; - } else if ( ctx == KdbContext.EntryString && name.equalsIgnoreCase(PwDatabaseV4XML.ElemString) ) { - ctxEntry.addExtraField(ctxStringName, ctxStringValue); - ctxStringName = null; - ctxStringValue = null; - - return KdbContext.Entry; - } else if ( ctx == KdbContext.EntryBinary && name.equalsIgnoreCase(PwDatabaseV4XML.ElemBinary) ) { - ctxEntry.putProtectedBinary(ctxBinaryName, ctxBinaryValue); - ctxBinaryName = null; - ctxBinaryValue = null; - - return KdbContext.Entry; - } else if ( ctx == KdbContext.EntryAutoType && name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoType) ) { - return KdbContext.Entry; - } else if ( ctx == KdbContext.EntryAutoTypeItem && name.equalsIgnoreCase(PwDatabaseV4XML.ElemAutoTypeItem) ) { - ctxEntry.getAutoType().put(ctxATName, ctxATSeq); - ctxATName = null; - ctxATSeq = null; - - return KdbContext.EntryAutoType; - } else if ( ctx == KdbContext.EntryCustomData && name.equalsIgnoreCase(PwDatabaseV4XML.ElemCustomData)) { - return KdbContext.Entry; - } else if ( ctx == KdbContext.EntryCustomDataItem && name.equalsIgnoreCase(PwDatabaseV4XML.ElemStringDictExItem)) { - if (entryCustomDataKey != null && entryCustomDataValue != null) { - ctxEntry.putCustomData(entryCustomDataKey, entryCustomDataValue); - } - - entryCustomDataKey = null; - entryCustomDataValue = null; - - return KdbContext.EntryCustomData; - } else if ( ctx == KdbContext.EntryHistory && name.equalsIgnoreCase(PwDatabaseV4XML.ElemHistory) ) { - entryInHistory = false; - return KdbContext.Entry; - } else if ( ctx == KdbContext.RootDeletedObjects && name.equalsIgnoreCase(PwDatabaseV4XML.ElemDeletedObjects) ) { - return KdbContext.Root; - } else if ( ctx == KdbContext.DeletedObject && name.equalsIgnoreCase(PwDatabaseV4XML.ElemDeletedObject) ) { - ctxDeletedObject = null; - return KdbContext.RootDeletedObjects; - } else { - String contextName = ""; - if (ctx != null) { - contextName = ctx.name(); - } - throw new RuntimeException("Invalid end element: Context " + contextName + "End element: " + name); - } - } - - private PwDate ReadPwTime(XmlPullParser xpp) throws IOException, XmlPullParserException { - return new PwDate(ReadTime(xpp)); - } - - private Date ReadTime(XmlPullParser xpp) throws IOException, XmlPullParserException { - String sDate = ReadString(xpp); - Date utcDate = null; - - if (version >= PwDbHeaderV4.FILE_VERSION_32_4) { - byte[] buf = Base64Coder.decode(sDate); - if (buf.length != 8) { - byte[] buf8 = new byte[8]; - System.arraycopy(buf, 0, buf8, 0, Math.min(buf.length, 8)); - buf = buf8; - } - - long seconds = LEDataInputStream.readLong(buf, 0); - utcDate = DateUtil.convertKDBX4Time(seconds); - - } else { - - try { - utcDate = PwDatabaseV4XML.dateFormatter.get().parse(sDate); - } catch (ParseException e) { - // Catch with null test below - } - - if (utcDate == null) { - utcDate = new Date(0L); - } - } - - return utcDate; - - } - - private void ReadUnknown(XmlPullParser xpp) throws XmlPullParserException, IOException { - if ( xpp.isEmptyElementTag() ) return; - - ProcessNode(xpp); - while (xpp.next() != XmlPullParser.END_DOCUMENT ) { - if ( xpp.getEventType() == XmlPullParser.END_TAG ) break; - if ( xpp.getEventType() == XmlPullParser.START_TAG ) continue; - - ReadUnknown(xpp); - } - } - - private boolean ReadBool(XmlPullParser xpp, boolean bDefault) throws IOException, XmlPullParserException { - String str = ReadString(xpp); - - return str.equalsIgnoreCase("true") - || !str.equalsIgnoreCase("false") - && bDefault; - } - - private UUID ReadUuid(XmlPullParser xpp) throws IOException, XmlPullParserException { - String encoded = ReadString(xpp); - - if (encoded == null || encoded.length() == 0 ) { - return PwDatabase.UUID_ZERO; - } - - // TODO: Switch to framework Base64 once API level 8 is the minimum - byte[] buf = Base64Coder.decode(encoded); - - return Types.bytestoUUID(buf); - } - - private int ReadInt(XmlPullParser xpp, int def) throws IOException, XmlPullParserException { - String str = ReadString(xpp); - - int u; - try { - u = Integer.parseInt(str); - } catch( NumberFormatException e) { - u = def; - } - - return u; - } - - private static final long MAX_UINT = 4294967296L; // 2^32 - private long ReadUInt(XmlPullParser xpp, long uDefault) throws IOException, XmlPullParserException { - long u; - - u = ReadULong(xpp, uDefault); - if ( u < 0 || u > MAX_UINT ) { - throw new NumberFormatException("Outside of the uint size"); - } - - return u; - - } - - private long ReadLong(XmlPullParser xpp, long def) throws IOException, XmlPullParserException { - String str = ReadString(xpp); - - long u; - try { - u = Long.parseLong(str); - } catch( NumberFormatException e) { - u = def; - } - - return u; - } - - private long ReadULong(XmlPullParser xpp, long uDefault) throws IOException, XmlPullParserException { - long u = ReadLong(xpp, uDefault); - - if ( u < 0 ) { - u = uDefault; - } - - return u; - - } - - private ProtectedString ReadProtectedString(XmlPullParser xpp) throws XmlPullParserException, IOException { - byte[] buf = ProcessNode(xpp); - - if ( buf != null) { - try { - return new ProtectedString(true, new String(buf, "UTF-8")); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - throw new IOException(e.getLocalizedMessage()); - } - } - - return new ProtectedString(false, ReadString(xpp)); - } - - private ProtectedBinary createProtectedBinaryFromData(boolean protection, byte[] data) throws IOException { - if (data.length > MemUtil.BUFFER_SIZE_BYTES) { - File file = new File(streamDir, getUnusedCacheFileName()); - try (FileOutputStream outputStream = new FileOutputStream(file)) { - outputStream.write(data); - } - return new ProtectedBinary(protection, file, data.length); - } else { - return new ProtectedBinary(protection, data); - } - } - - private ProtectedBinary ReadProtectedBinary(XmlPullParser xpp) throws XmlPullParserException, IOException { - String ref = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrRef); - if (ref != null) { - xpp.next(); // Consume end tag - - int id = Integer.parseInt(ref); - return mDatabase.getBinPool().get(id); - } - - boolean compressed = false; - String comp = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrCompressed); - if (comp != null) { - compressed = comp.equalsIgnoreCase(PwDatabaseV4XML.ValTrue); - } - - byte[] buf = ProcessNode(xpp); - - if ( buf != null ) { - createProtectedBinaryFromData(true, buf); - } - - String base64 = ReadString(xpp); - if ( base64.length() == 0 ) - return new ProtectedBinary(); - - byte[] data = Base64Coder.decode(base64); - - if (compressed) { - data = MemUtil.decompress(data); - } - - return createProtectedBinaryFromData(false, data); - } - - private String ReadString(XmlPullParser xpp) throws IOException, XmlPullParserException { - byte[] buf = ProcessNode(xpp); - - if ( buf != null ) { - try { - return new String(buf, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IOException(e.getLocalizedMessage()); - } - } - - //readNextNode = false; - return xpp.nextText(); - - } - - private String ReadStringRaw(XmlPullParser xpp) throws XmlPullParserException, IOException { - - //readNextNode = false; - return xpp.nextText(); - } - - private byte[] ProcessNode(XmlPullParser xpp) throws XmlPullParserException, IOException { - //(xpp.getEventType() == XmlPullParser.START_TAG); - - byte[] buf = null; - - if ( xpp.getAttributeCount() > 0 ) { - String protect = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrProtected); - if ( protect != null && protect.equalsIgnoreCase(PwDatabaseV4XML.ValTrue) ) { - // TODO stream for encrypted data - String encrypted = ReadStringRaw(xpp); - - if ( encrypted.length() > 0 ) { - buf = Base64Coder.decode(encrypted); - byte[] plainText = new byte[buf.length]; - - randomStream.processBytes(buf, 0, buf.length, plainText, 0); - - return plainText; - } else { - buf = new byte[0]; - } - } - } - - return buf; - } - - private KdbContext SwitchContext(KdbContext ctxCurrent, KdbContext ctxNew, - XmlPullParser xpp) throws XmlPullParserException, IOException { - - if ( xpp.isEmptyElementTag() ) { - xpp.next(); // Consume the end tag - return ctxCurrent; - } - return ctxNew; - } - - - private Boolean StringToBoolean(String str) { - if ( str == null || str.length() == 0 ) { - return null; - } - - String trimmed = str.trim(); - if ( trimmed.equalsIgnoreCase("true") ) { - return true; - } else if ( trimmed.equalsIgnoreCase("false") ) { - return false; - } - - return null; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.kt new file mode 100644 index 000000000..c5b7a65f1 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.kt @@ -0,0 +1,1071 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.load + +import biz.source_code.base64Coder.Base64Coder +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.crypto.CipherFactory +import com.kunzisoft.keepass.crypto.PwStreamCipherFactory +import com.kunzisoft.keepass.crypto.engine.CipherEngine +import com.kunzisoft.keepass.database.PwCompressionAlgorithm +import com.kunzisoft.keepass.database.element.* +import com.kunzisoft.keepass.database.exception.ArcFourException +import com.kunzisoft.keepass.database.exception.InvalidDBException +import com.kunzisoft.keepass.database.exception.InvalidPasswordException +import com.kunzisoft.keepass.database.security.ProtectedBinary +import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.stream.BetterCipherInputStream +import com.kunzisoft.keepass.stream.HashedBlockInputStream +import com.kunzisoft.keepass.stream.HmacBlockInputStream +import com.kunzisoft.keepass.stream.LEDataInputStream +import com.kunzisoft.keepass.tasks.ProgressTaskUpdater +import com.kunzisoft.keepass.utils.DateUtil +import com.kunzisoft.keepass.utils.EmptyUtils +import com.kunzisoft.keepass.utils.MemUtil +import com.kunzisoft.keepass.utils.Types +import org.spongycastle.crypto.StreamCipher +import org.xmlpull.v1.XmlPullParser +import org.xmlpull.v1.XmlPullParserException +import org.xmlpull.v1.XmlPullParserFactory +import java.io.* +import java.nio.charset.Charset +import java.security.InvalidAlgorithmParameterException +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import java.text.ParseException +import java.util.* +import java.util.zip.GZIPInputStream +import javax.crypto.Cipher +import javax.crypto.NoSuchPaddingException + +class ImporterV4(private val streamDir: File) : Importer() { + + private var randomStream: StreamCipher? = null + private lateinit var mDatabase: PwDatabaseV4 + + private var hashOfHeader: ByteArray? = null + private var version: Long = 0 + + private val unusedCacheFileName: String + get() = mDatabase.binPool.findUnusedKey().toString() + + private var readNextNode = true + private val ctxGroups = Stack() + private var ctxGroup: PwGroupV4? = null + private var ctxEntry: PwEntryV4? = null + private var ctxStringName: String? = null + private var ctxStringValue: ProtectedString? = null + private var ctxBinaryName: String? = null + private var ctxBinaryValue: ProtectedBinary? = null + private var ctxATName: String? = null + private var ctxATSeq: String? = null + private var entryInHistory = false + private var ctxHistoryBase: PwEntryV4? = null + private var ctxDeletedObject: PwDeletedObject? = null + private var customIconID = PwDatabase.UUID_ZERO + private var customIconData: ByteArray? = null + private var customDataKey: String? = null + private var customDataValue: String? = null + private var groupCustomDataKey: String? = null + private var groupCustomDataValue: String? = null + private var entryCustomDataKey: String? = null + private var entryCustomDataValue: String? = null + + @Throws(IOException::class, InvalidDBException::class) + override fun openDatabase(databaseInputStream: InputStream, + password: String?, + keyInputStream: InputStream?, + progressTaskUpdater: ProgressTaskUpdater?): PwDatabaseV4 { + + progressTaskUpdater?.updateMessage(R.string.retrieving_db_key) + mDatabase = PwDatabaseV4() + + val header = PwDbHeaderV4(mDatabase) + mDatabase.binPool.clear() + + val hh = header.loadFromFile(databaseInputStream) + version = header.getVersion() + + hashOfHeader = hh.hash + val pbHeader = hh.header + + mDatabase.retrieveMasterKey(password, keyInputStream) + mDatabase.makeFinalKey(header.masterSeed) + + progressTaskUpdater?.updateMessage(R.string.decrypting_db) + val engine: CipherEngine + val cipher: Cipher + try { + engine = CipherFactory.getInstance(mDatabase.dataCipher) + mDatabase.setDataEngine(engine) + mDatabase.encryptionAlgorithm = engine.pwEncryptionAlgorithm + cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.finalKey, header.encryptionIV) + } catch (e: NoSuchAlgorithmException) { + throw IOException("Invalid algorithm.", e) + } catch (e: NoSuchPaddingException) { + throw IOException("Invalid algorithm.", e) + } catch (e: InvalidKeyException) { + throw IOException("Invalid algorithm.", e) + } catch (e: InvalidAlgorithmParameterException) { + throw IOException("Invalid algorithm.", e) + } + + val isPlain: InputStream + if (version < PwDbHeaderV4.FILE_VERSION_32_4) { + + val decrypted = attachCipherStream(databaseInputStream, cipher) + val dataDecrypted = LEDataInputStream(decrypted) + val storedStartBytes: ByteArray? + try { + storedStartBytes = dataDecrypted.readBytes(32) + if (storedStartBytes == null || storedStartBytes.size != 32) { + throw InvalidPasswordException() + } + } catch (e: IOException) { + throw InvalidPasswordException() + } + + if (!Arrays.equals(storedStartBytes, header.streamStartBytes)) { + throw InvalidPasswordException() + } + + isPlain = HashedBlockInputStream(dataDecrypted) + } else { // KDBX 4 + val isData = LEDataInputStream(databaseInputStream) + val storedHash = isData.readBytes(32) + if (!Arrays.equals(storedHash, hashOfHeader)) { + throw InvalidDBException() + } + + val hmacKey = mDatabase.hmacKey + val headerHmac = PwDbHeaderV4.computeHeaderHmac(pbHeader, hmacKey) + val storedHmac = isData.readBytes(32) + if (storedHmac == null || storedHmac.size != 32) { + throw InvalidDBException() + } + // Mac doesn't match + if (!Arrays.equals(headerHmac, storedHmac)) { + throw InvalidDBException() + } + + val hmIs = HmacBlockInputStream(isData, true, hmacKey) + + isPlain = attachCipherStream(hmIs, cipher) + } + + val isXml: InputStream + if (mDatabase.compressionAlgorithm === PwCompressionAlgorithm.Gzip) { + isXml = GZIPInputStream(isPlain) + } else { + isXml = isPlain + } + + if (version >= PwDbHeaderV4.FILE_VERSION_32_4) { + loadInnerHeader(isXml, header) + } + + if (header.innerRandomStreamKey == null) { + throw IOException("Invalid stream key.") + } + + randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) + + if (randomStream == null) { + throw ArcFourException() + } + + readXmlStreamed(isXml) + + return mDatabase + } + + private fun attachCipherStream(`is`: InputStream, cipher: Cipher): InputStream { + return BetterCipherInputStream(`is`, cipher, 50 * 1024) + } + + @Throws(IOException::class) + private fun loadInnerHeader(inputStream: InputStream, header: PwDbHeaderV4) { + val lis = LEDataInputStream(inputStream) + + while (true) { + if (!readInnerHeader(lis, header)) break + } + } + + @Throws(IOException::class) + private fun readInnerHeader(lis: LEDataInputStream, header: PwDbHeaderV4): Boolean { + val fieldId = lis.read().toByte() + + val size = lis.readInt() + if (size < 0) throw IOException("Corrupted file") + + var data = ByteArray(0) + if (size > 0) { + if (fieldId != PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary) + data = lis.readBytes(size) + } + + var result = true + when (fieldId) { + PwDbHeaderV4.PwDbInnerHeaderV4Fields.EndOfHeader -> result = false + PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID -> header.setRandomStreamID(data) + PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey -> header.innerRandomStreamKey = data + PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary -> { + val flag = lis.readBytes(1)[0].toInt() != 0 + val protectedFlag = flag && PwDbHeaderV4.KdbxBinaryFlags.Protected.toInt() != PwDbHeaderV4.KdbxBinaryFlags.None.toInt() + val byteLength = size - 1 + // Read in a file + val file = File(streamDir, unusedCacheFileName) + FileOutputStream(file).use { outputStream -> lis.readBytes(byteLength) { outputStream.write(it) } } + val protectedBinary = ProtectedBinary(protectedFlag, file, byteLength) + mDatabase.binPool.add(protectedBinary) + } + + else -> { + } + } + + return result + } + + private enum class KdbContext { + Null, + KeePassFile, + Meta, + Root, + MemoryProtection, + CustomIcons, + CustomIcon, + CustomData, + CustomDataItem, + RootDeletedObjects, + DeletedObject, + Group, + GroupTimes, + GroupCustomData, + GroupCustomDataItem, + Entry, + EntryTimes, + EntryString, + EntryBinary, + EntryAutoType, + EntryAutoTypeItem, + EntryHistory, + EntryCustomData, + EntryCustomDataItem, + Binaries + } + + @Throws(IOException::class, InvalidDBException::class) + private fun readXmlStreamed(readerStream: InputStream) { + try { + readDocumentStreamed(createPullParser(readerStream)) + } catch (e: XmlPullParserException) { + e.printStackTrace() + throw IOException(e.localizedMessage) + } + + } + + @Throws(XmlPullParserException::class, IOException::class, InvalidDBException::class) + private fun readDocumentStreamed(xpp: XmlPullParser) { + + ctxGroups.clear() + + var ctx = KdbContext.Null + + readNextNode = true + + while (true) { + if (readNextNode) { + if (xpp.next() == XmlPullParser.END_DOCUMENT) break + } else { + readNextNode = true + } + + when (xpp.eventType) { + XmlPullParser.START_TAG -> ctx = readXmlElement(ctx, xpp) + + XmlPullParser.END_TAG -> ctx = endXmlElement(ctx, xpp) + + else -> { + } + } + } + + // Error checks + if (ctx != KdbContext.Null) throw IOException("Malformed") + if (ctxGroups.size != 0) throw IOException("Malformed") + } + + @Throws(XmlPullParserException::class, IOException::class, InvalidDBException::class) + private fun readXmlElement(ctx: KdbContext, xpp: XmlPullParser): KdbContext { + val name = xpp.name + when (ctx) { + KdbContext.Null -> if (name.equals(PwDatabaseV4XML.ElemDocNode, ignoreCase = true)) { + return switchContext(ctx, KdbContext.KeePassFile, xpp) + } else + readUnknown(xpp) + + KdbContext.KeePassFile -> if (name.equals(PwDatabaseV4XML.ElemMeta, ignoreCase = true)) { + return switchContext(ctx, KdbContext.Meta, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemRoot, ignoreCase = true)) { + return switchContext(ctx, KdbContext.Root, xpp) + } else { + readUnknown(xpp) + } + + KdbContext.Meta -> if (name.equals(PwDatabaseV4XML.ElemGenerator, ignoreCase = true)) { + readString(xpp) // Ignore + } else if (name.equals(PwDatabaseV4XML.ElemHeaderHash, ignoreCase = true)) { + val encodedHash = readString(xpp) + if (!EmptyUtils.isNullOrEmpty(encodedHash) && hashOfHeader != null) { + val hash = Base64Coder.decode(encodedHash) + if (!Arrays.equals(hash, hashOfHeader)) { + throw InvalidDBException() + } + } + } else if (name.equals(PwDatabaseV4XML.ElemSettingsChanged, ignoreCase = true)) { + mDatabase.settingsChanged = readPwTime(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbName, ignoreCase = true)) { + mDatabase.name = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbNameChanged, ignoreCase = true)) { + mDatabase.nameChanged = readPwTime(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbDesc, ignoreCase = true)) { + mDatabase.description = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbDescChanged, ignoreCase = true)) { + mDatabase.descriptionChanged = readPwTime(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbDefaultUser, ignoreCase = true)) { + mDatabase.defaultUserName = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbDefaultUserChanged, ignoreCase = true)) { + mDatabase.defaultUserNameChanged = readPwTime(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbColor, ignoreCase = true)) { + // TODO: Add support to interpret the color if we want to allow changing the database color + mDatabase.color = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbMntncHistoryDays, ignoreCase = true)) { + mDatabase.maintenanceHistoryDays = readUInt(xpp, DEFAULT_HISTORY_DAYS) + } else if (name.equals(PwDatabaseV4XML.ElemDbKeyChanged, ignoreCase = true)) { + mDatabase.keyLastChanged = readPwTime(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDbKeyChangeRec, ignoreCase = true)) { + mDatabase.keyChangeRecDays = readLong(xpp, -1) + } else if (name.equals(PwDatabaseV4XML.ElemDbKeyChangeForce, ignoreCase = true)) { + mDatabase.keyChangeForceDays = readLong(xpp, -1) + } else if (name.equals(PwDatabaseV4XML.ElemDbKeyChangeForceOnce, ignoreCase = true)) { + mDatabase.isKeyChangeForceOnce = readBool(xpp, false) + } else if (name.equals(PwDatabaseV4XML.ElemMemoryProt, ignoreCase = true)) { + return switchContext(ctx, KdbContext.MemoryProtection, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemCustomIcons, ignoreCase = true)) { + return switchContext(ctx, KdbContext.CustomIcons, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemRecycleBinEnabled, ignoreCase = true)) { + mDatabase.isRecycleBinEnabled = readBool(xpp, true) + } else if (name.equals(PwDatabaseV4XML.ElemRecycleBinUuid, ignoreCase = true)) { + mDatabase.recycleBinUUID = readUuid(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemRecycleBinChanged, ignoreCase = true)) { + mDatabase.recycleBinChanged = readTime(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemEntryTemplatesGroup, ignoreCase = true)) { + mDatabase.entryTemplatesGroup = readUuid(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemEntryTemplatesGroupChanged, ignoreCase = true)) { + mDatabase.entryTemplatesGroupChanged = readPwTime(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemHistoryMaxItems, ignoreCase = true)) { + mDatabase.historyMaxItems = readInt(xpp, -1) + } else if (name.equals(PwDatabaseV4XML.ElemHistoryMaxSize, ignoreCase = true)) { + mDatabase.historyMaxSize = readLong(xpp, -1) + } else if (name.equals(PwDatabaseV4XML.ElemLastSelectedGroup, ignoreCase = true)) { + mDatabase.lastSelectedGroup = readUuid(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemLastTopVisibleGroup, ignoreCase = true)) { + mDatabase.lastTopVisibleGroup = readUuid(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemBinaries, ignoreCase = true)) { + return switchContext(ctx, KdbContext.Binaries, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemCustomData, ignoreCase = true)) { + return switchContext(ctx, KdbContext.CustomData, xpp) + } + + KdbContext.MemoryProtection -> if (name.equals(PwDatabaseV4XML.ElemProtTitle, ignoreCase = true)) { + mDatabase.memoryProtection.protectTitle = readBool(xpp, false) + } else if (name.equals(PwDatabaseV4XML.ElemProtUserName, ignoreCase = true)) { + mDatabase.memoryProtection.protectUserName = readBool(xpp, false) + } else if (name.equals(PwDatabaseV4XML.ElemProtPassword, ignoreCase = true)) { + mDatabase.memoryProtection.protectPassword = readBool(xpp, false) + } else if (name.equals(PwDatabaseV4XML.ElemProtURL, ignoreCase = true)) { + mDatabase.memoryProtection.protectUrl = readBool(xpp, false) + } else if (name.equals(PwDatabaseV4XML.ElemProtNotes, ignoreCase = true)) { + mDatabase.memoryProtection.protectNotes = readBool(xpp, false) + } else if (name.equals(PwDatabaseV4XML.ElemProtAutoHide, ignoreCase = true)) { + mDatabase.memoryProtection.autoEnableVisualHiding = readBool(xpp, false) + } else { + readUnknown(xpp) + } + + KdbContext.CustomIcons -> if (name.equals(PwDatabaseV4XML.ElemCustomIconItem, ignoreCase = true)) { + return switchContext(ctx, KdbContext.CustomIcon, xpp) + } else { + readUnknown(xpp) + } + + KdbContext.CustomIcon -> if (name.equals(PwDatabaseV4XML.ElemCustomIconItemID, ignoreCase = true)) { + customIconID = readUuid(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemCustomIconItemData, ignoreCase = true)) { + val strData = readString(xpp) + if (strData.isNotEmpty()) { + customIconData = Base64Coder.decode(strData) + } else { + assert(false) + } + } else { + readUnknown(xpp) + } + + KdbContext.Binaries -> if (name.equals(PwDatabaseV4XML.ElemBinary, ignoreCase = true)) { + val key = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrId) + if (key != null) { + val pbData = readProtectedBinary(xpp) + val id = Integer.parseInt(key) + mDatabase.binPool.put(id, pbData!!) + } else { + readUnknown(xpp) + } + } else { + readUnknown(xpp) + } + + KdbContext.CustomData -> if (name.equals(PwDatabaseV4XML.ElemStringDictExItem, ignoreCase = true)) { + return switchContext(ctx, KdbContext.CustomDataItem, xpp) + } else { + readUnknown(xpp) + } + + KdbContext.CustomDataItem -> if (name.equals(PwDatabaseV4XML.ElemKey, ignoreCase = true)) { + customDataKey = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemValue, ignoreCase = true)) { + customDataValue = readString(xpp) + } else { + readUnknown(xpp) + } + + KdbContext.Root -> if (name.equals(PwDatabaseV4XML.ElemGroup, ignoreCase = true)) { + if (ctxGroups.size != 0) + throw IOException("Group list should be empty.") + + mDatabase.rootGroup = mDatabase.createGroup() + ctxGroups.push(mDatabase.rootGroup) + ctxGroup = ctxGroups.peek() + + return switchContext(ctx, KdbContext.Group, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDeletedObjects, ignoreCase = true)) { + return switchContext(ctx, KdbContext.RootDeletedObjects, xpp) + } else { + readUnknown(xpp) + } + + KdbContext.Group -> if (name.equals(PwDatabaseV4XML.ElemUuid, ignoreCase = true)) { + ctxGroup!!.nodeId = PwNodeIdUUID(readUuid(xpp)) + mDatabase.addGroupIndex(ctxGroup) + } else if (name.equals(PwDatabaseV4XML.ElemName, ignoreCase = true)) { + ctxGroup!!.title = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemNotes, ignoreCase = true)) { + ctxGroup!!.notes = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemIcon, ignoreCase = true)) { + ctxGroup!!.icon = mDatabase.iconFactory.getIcon(readUInt(xpp, 0).toInt()) + } else if (name.equals(PwDatabaseV4XML.ElemCustomIconID, ignoreCase = true)) { + ctxGroup!!.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp)) + } else if (name.equals(PwDatabaseV4XML.ElemTimes, ignoreCase = true)) { + return switchContext(ctx, KdbContext.GroupTimes, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemIsExpanded, ignoreCase = true)) { + ctxGroup!!.isExpanded = readBool(xpp, true) + } else if (name.equals(PwDatabaseV4XML.ElemGroupDefaultAutoTypeSeq, ignoreCase = true)) { + ctxGroup!!.defaultAutoTypeSequence = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemEnableAutoType, ignoreCase = true)) { + ctxGroup!!.enableAutoType = stringToBoolean(readString(xpp)) + } else if (name.equals(PwDatabaseV4XML.ElemEnableSearching, ignoreCase = true)) { + ctxGroup!!.enableSearching = stringToBoolean(readString(xpp)) + } else if (name.equals(PwDatabaseV4XML.ElemLastTopVisibleEntry, ignoreCase = true)) { + ctxGroup!!.lastTopVisibleEntry = readUuid(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemCustomData, ignoreCase = true)) { + return switchContext(ctx, KdbContext.GroupCustomData, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemGroup, ignoreCase = true)) { + ctxGroup = mDatabase.createGroup() + val groupPeek = ctxGroups.peek() + groupPeek.addChildGroup(ctxGroup!!) + ctxGroup!!.parent = groupPeek + ctxGroups.push(ctxGroup) + + return switchContext(ctx, KdbContext.Group, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemEntry, ignoreCase = true)) { + ctxEntry = mDatabase.createEntry() + ctxGroup!!.addChildEntry(ctxEntry!!) + ctxEntry!!.parent = ctxGroup + + entryInHistory = false + return switchContext(ctx, KdbContext.Entry, xpp) + } else { + readUnknown(xpp) + } + KdbContext.GroupCustomData -> if (name.equals(PwDatabaseV4XML.ElemStringDictExItem, ignoreCase = true)) { + return switchContext(ctx, KdbContext.GroupCustomDataItem, xpp) + } else { + readUnknown(xpp) + } + KdbContext.GroupCustomDataItem -> when { + name.equals(PwDatabaseV4XML.ElemKey, ignoreCase = true) -> groupCustomDataKey = readString(xpp) + name.equals(PwDatabaseV4XML.ElemValue, ignoreCase = true) -> groupCustomDataValue = readString(xpp) + else -> readUnknown(xpp) + } + + + KdbContext.Entry -> if (name.equals(PwDatabaseV4XML.ElemUuid, ignoreCase = true)) { + ctxEntry!!.nodeId = PwNodeIdUUID(readUuid(xpp)) + mDatabase.addEntryIndex(ctxEntry) + } else if (name.equals(PwDatabaseV4XML.ElemIcon, ignoreCase = true)) { + ctxEntry!!.icon = mDatabase.iconFactory.getIcon(readUInt(xpp, 0).toInt()) + } else if (name.equals(PwDatabaseV4XML.ElemCustomIconID, ignoreCase = true)) { + ctxEntry!!.iconCustom = mDatabase.iconFactory.getIcon(readUuid(xpp)) + } else if (name.equals(PwDatabaseV4XML.ElemFgColor, ignoreCase = true)) { + ctxEntry!!.foregroundColor = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemBgColor, ignoreCase = true)) { + ctxEntry!!.backgroundColor = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemOverrideUrl, ignoreCase = true)) { + ctxEntry!!.overrideURL = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemTags, ignoreCase = true)) { + ctxEntry!!.tags = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemTimes, ignoreCase = true)) { + return switchContext(ctx, KdbContext.EntryTimes, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemString, ignoreCase = true)) { + return switchContext(ctx, KdbContext.EntryString, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemBinary, ignoreCase = true)) { + return switchContext(ctx, KdbContext.EntryBinary, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemAutoType, ignoreCase = true)) { + return switchContext(ctx, KdbContext.EntryAutoType, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemCustomData, ignoreCase = true)) { + return switchContext(ctx, KdbContext.EntryCustomData, xpp) + } else if (name.equals(PwDatabaseV4XML.ElemHistory, ignoreCase = true)) { + if (!entryInHistory) { + ctxHistoryBase = ctxEntry + return switchContext(ctx, KdbContext.EntryHistory, xpp) + } else { + readUnknown(xpp) + } + } else { + readUnknown(xpp) + } + KdbContext.EntryCustomData -> if (name.equals(PwDatabaseV4XML.ElemStringDictExItem, ignoreCase = true)) { + return switchContext(ctx, KdbContext.EntryCustomDataItem, xpp) + } else { + readUnknown(xpp) + } + KdbContext.EntryCustomDataItem -> when { + name.equals(PwDatabaseV4XML.ElemKey, ignoreCase = true) -> entryCustomDataKey = readString(xpp) + name.equals(PwDatabaseV4XML.ElemValue, ignoreCase = true) -> entryCustomDataValue = readString(xpp) + else -> readUnknown(xpp) + } + + KdbContext.GroupTimes, KdbContext.EntryTimes -> { + val tl: NodeV4Interface? + if (ctx == KdbContext.GroupTimes) { + tl = ctxGroup + } else { + tl = ctxEntry + } + + when { + name.equals(PwDatabaseV4XML.ElemLastModTime, ignoreCase = true) -> tl!!.lastModificationTime = readPwTime(xpp) + name.equals(PwDatabaseV4XML.ElemCreationTime, ignoreCase = true) -> tl!!.creationTime = readPwTime(xpp) + name.equals(PwDatabaseV4XML.ElemLastAccessTime, ignoreCase = true) -> tl!!.lastAccessTime = readPwTime(xpp) + name.equals(PwDatabaseV4XML.ElemExpiryTime, ignoreCase = true) -> tl!!.expiryTime = readPwTime(xpp) + name.equals(PwDatabaseV4XML.ElemExpires, ignoreCase = true) -> tl!!.isExpires = readBool(xpp, false) + name.equals(PwDatabaseV4XML.ElemUsageCount, ignoreCase = true) -> tl!!.usageCount = readULong(xpp, 0) + name.equals(PwDatabaseV4XML.ElemLocationChanged, ignoreCase = true) -> tl!!.locationChanged = readPwTime(xpp) + else -> readUnknown(xpp) + } + } + + KdbContext.EntryString -> if (name.equals(PwDatabaseV4XML.ElemKey, ignoreCase = true)) { + ctxStringName = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemValue, ignoreCase = true)) { + ctxStringValue = readProtectedString(xpp) + } else { + readUnknown(xpp) + } + + KdbContext.EntryBinary -> if (name.equals(PwDatabaseV4XML.ElemKey, ignoreCase = true)) { + ctxBinaryName = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemValue, ignoreCase = true)) { + ctxBinaryValue = readProtectedBinary(xpp) + } + + KdbContext.EntryAutoType -> if (name.equals(PwDatabaseV4XML.ElemAutoTypeEnabled, ignoreCase = true)) { + ctxEntry!!.autoType.enabled = readBool(xpp, true) + } else if (name.equals(PwDatabaseV4XML.ElemAutoTypeObfuscation, ignoreCase = true)) { + ctxEntry!!.autoType.obfuscationOptions = readUInt(xpp, 0) + } else if (name.equals(PwDatabaseV4XML.ElemAutoTypeDefaultSeq, ignoreCase = true)) { + ctxEntry!!.autoType.defaultSequence = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemAutoTypeItem, ignoreCase = true)) { + return switchContext(ctx, KdbContext.EntryAutoTypeItem, xpp) + } else { + readUnknown(xpp) + } + + KdbContext.EntryAutoTypeItem -> if (name.equals(PwDatabaseV4XML.ElemWindow, ignoreCase = true)) { + ctxATName = readString(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemKeystrokeSequence, ignoreCase = true)) { + ctxATSeq = readString(xpp) + } else { + readUnknown(xpp) + } + + KdbContext.EntryHistory -> if (name.equals(PwDatabaseV4XML.ElemEntry, ignoreCase = true)) { + ctxEntry = PwEntryV4() + ctxHistoryBase!!.addToHistory(ctxEntry!!) + + entryInHistory = true + return switchContext(ctx, KdbContext.Entry, xpp) + } else { + readUnknown(xpp) + } + + KdbContext.RootDeletedObjects -> if (name.equals(PwDatabaseV4XML.ElemDeletedObject, ignoreCase = true)) { + ctxDeletedObject = PwDeletedObject() + mDatabase.addDeletedObject(ctxDeletedObject) + + return switchContext(ctx, KdbContext.DeletedObject, xpp) + } else { + readUnknown(xpp) + } + + KdbContext.DeletedObject -> if (name.equals(PwDatabaseV4XML.ElemUuid, ignoreCase = true)) { + ctxDeletedObject!!.uuid = readUuid(xpp) + } else if (name.equals(PwDatabaseV4XML.ElemDeletionTime, ignoreCase = true)) { + ctxDeletedObject!!.deletionTime = readTime(xpp) + } else { + readUnknown(xpp) + } + } + + return ctx + } + + @Throws(XmlPullParserException::class) + private fun endXmlElement(ctx: KdbContext?, xpp: XmlPullParser): KdbContext { + // (xpp.getEventType() == XmlPullParser.END_TAG); + + val name = xpp.name + if (ctx == KdbContext.KeePassFile && name.equals(PwDatabaseV4XML.ElemDocNode, ignoreCase = true)) { + return KdbContext.Null + } else if (ctx == KdbContext.Meta && name.equals(PwDatabaseV4XML.ElemMeta, ignoreCase = true)) { + return KdbContext.KeePassFile + } else if (ctx == KdbContext.Root && name.equals(PwDatabaseV4XML.ElemRoot, ignoreCase = true)) { + return KdbContext.KeePassFile + } else if (ctx == KdbContext.MemoryProtection && name.equals(PwDatabaseV4XML.ElemMemoryProt, ignoreCase = true)) { + return KdbContext.Meta + } else if (ctx == KdbContext.CustomIcons && name.equals(PwDatabaseV4XML.ElemCustomIcons, ignoreCase = true)) { + return KdbContext.Meta + } else if (ctx == KdbContext.CustomIcon && name.equals(PwDatabaseV4XML.ElemCustomIconItem, ignoreCase = true)) { + if (customIconID != PwDatabase.UUID_ZERO) { + val icon = PwIconCustom(customIconID, customIconData!!) + mDatabase.addCustomIcon(icon) + mDatabase.iconFactory.put(icon) + } + + customIconID = PwDatabase.UUID_ZERO + customIconData = null + + return KdbContext.CustomIcons + } else if (ctx == KdbContext.Binaries && name.equals(PwDatabaseV4XML.ElemBinaries, ignoreCase = true)) { + return KdbContext.Meta + } else if (ctx == KdbContext.CustomData && name.equals(PwDatabaseV4XML.ElemCustomData, ignoreCase = true)) { + return KdbContext.Meta + } else if (ctx == KdbContext.CustomDataItem && name.equals(PwDatabaseV4XML.ElemStringDictExItem, ignoreCase = true)) { + if (customDataKey != null && customDataValue != null) { + mDatabase.putCustomData(customDataKey, customDataValue) + } + + customDataKey = null + customDataValue = null + + return KdbContext.CustomData + } else if (ctx == KdbContext.Group && name.equals(PwDatabaseV4XML.ElemGroup, ignoreCase = true)) { + if (ctxGroup!!.id == PwDatabase.UUID_ZERO) { + ctxGroup!!.nodeId = mDatabase.newGroupId() + mDatabase.addGroupIndex(ctxGroup) + } + + ctxGroups.pop() + + if (ctxGroups.size == 0) { + ctxGroup = null + return KdbContext.Root + } else { + ctxGroup = ctxGroups.peek() + return KdbContext.Group + } + } else if (ctx == KdbContext.GroupTimes && name.equals(PwDatabaseV4XML.ElemTimes, ignoreCase = true)) { + return KdbContext.Group + } else if (ctx == KdbContext.GroupCustomData && name.equals(PwDatabaseV4XML.ElemCustomData, ignoreCase = true)) { + return KdbContext.Group + } else if (ctx == KdbContext.GroupCustomDataItem && name.equals(PwDatabaseV4XML.ElemStringDictExItem, ignoreCase = true)) { + if (groupCustomDataKey != null && groupCustomDataValue != null) { + ctxGroup!!.putCustomData(groupCustomDataKey!!, groupCustomDataKey!!) + } + + groupCustomDataKey = null + groupCustomDataValue = null + + return KdbContext.GroupCustomData + + } else if (ctx == KdbContext.Entry && name.equals(PwDatabaseV4XML.ElemEntry, ignoreCase = true)) { + if (ctxEntry!!.id == PwDatabase.UUID_ZERO) { + ctxEntry!!.nodeId = mDatabase.newEntryId() + mDatabase.addEntryIndex(ctxEntry) + } + + if (entryInHistory) { + ctxEntry = ctxHistoryBase + return KdbContext.EntryHistory + } + + return KdbContext.Group + } else if (ctx == KdbContext.EntryTimes && name.equals(PwDatabaseV4XML.ElemTimes, ignoreCase = true)) { + return KdbContext.Entry + } else if (ctx == KdbContext.EntryString && name.equals(PwDatabaseV4XML.ElemString, ignoreCase = true)) { + ctxEntry!!.addExtraField(ctxStringName!!, ctxStringValue!!) + ctxStringName = null + ctxStringValue = null + + return KdbContext.Entry + } else if (ctx == KdbContext.EntryBinary && name.equals(PwDatabaseV4XML.ElemBinary, ignoreCase = true)) { + ctxEntry!!.putProtectedBinary(ctxBinaryName!!, ctxBinaryValue!!) + ctxBinaryName = null + ctxBinaryValue = null + + return KdbContext.Entry + } else if (ctx == KdbContext.EntryAutoType && name.equals(PwDatabaseV4XML.ElemAutoType, ignoreCase = true)) { + return KdbContext.Entry + } else if (ctx == KdbContext.EntryAutoTypeItem && name.equals(PwDatabaseV4XML.ElemAutoTypeItem, ignoreCase = true)) { + ctxEntry!!.autoType.put(ctxATName!!, ctxATSeq!!) + ctxATName = null + ctxATSeq = null + + return KdbContext.EntryAutoType + } else if (ctx == KdbContext.EntryCustomData && name.equals(PwDatabaseV4XML.ElemCustomData, ignoreCase = true)) { + return KdbContext.Entry + } else if (ctx == KdbContext.EntryCustomDataItem && name.equals(PwDatabaseV4XML.ElemStringDictExItem, ignoreCase = true)) { + if (entryCustomDataKey != null && entryCustomDataValue != null) { + ctxEntry!!.putCustomData(entryCustomDataKey!!, entryCustomDataValue!!) + } + + entryCustomDataKey = null + entryCustomDataValue = null + + return KdbContext.EntryCustomData + } else if (ctx == KdbContext.EntryHistory && name.equals(PwDatabaseV4XML.ElemHistory, ignoreCase = true)) { + entryInHistory = false + return KdbContext.Entry + } else if (ctx == KdbContext.RootDeletedObjects && name.equals(PwDatabaseV4XML.ElemDeletedObjects, ignoreCase = true)) { + return KdbContext.Root + } else if (ctx == KdbContext.DeletedObject && name.equals(PwDatabaseV4XML.ElemDeletedObject, ignoreCase = true)) { + ctxDeletedObject = null + return KdbContext.RootDeletedObjects + } else { + var contextName = "" + if (ctx != null) { + contextName = ctx.name + } + throw RuntimeException("Invalid end element: Context " + contextName + "End element: " + name) + } + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readPwTime(xpp: XmlPullParser): PwDate { + return PwDate(readTime(xpp)) + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readTime(xpp: XmlPullParser): Date { + val sDate = readString(xpp) + var utcDate: Date? = null + + if (version >= PwDbHeaderV4.FILE_VERSION_32_4) { + var buf = Base64Coder.decode(sDate) + if (buf.size != 8) { + val buf8 = ByteArray(8) + System.arraycopy(buf, 0, buf8, 0, Math.min(buf.size, 8)) + buf = buf8 + } + + val seconds = LEDataInputStream.readLong(buf, 0) + utcDate = DateUtil.convertKDBX4Time(seconds) + + } else { + + try { + utcDate = PwDatabaseV4XML.dateFormatter.get().parse(sDate) + } catch (e: ParseException) { + // Catch with null test below + } + } + + return utcDate ?: Date(0L) + } + + @Throws(XmlPullParserException::class, IOException::class) + private fun readUnknown(xpp: XmlPullParser) { + if (xpp.isEmptyElementTag) return + + processNode(xpp) + while (xpp.next() != XmlPullParser.END_DOCUMENT) { + if (xpp.eventType == XmlPullParser.END_TAG) break + if (xpp.eventType == XmlPullParser.START_TAG) continue + + readUnknown(xpp) + } + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readBool(xpp: XmlPullParser, bDefault: Boolean): Boolean { + val str = readString(xpp) + + return str.equals("true", ignoreCase = true) || !str.equals("false", ignoreCase = true) && bDefault + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readUuid(xpp: XmlPullParser): UUID { + val encoded = readString(xpp) + + if (encoded.isEmpty()) { + return PwDatabase.UUID_ZERO + } + + // TODO: Switch to framework Base64 once API level 8 is the minimum + val buf = Base64Coder.decode(encoded) + + return Types.bytestoUUID(buf) + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readInt(xpp: XmlPullParser, def: Int): Int { + val str = readString(xpp) + + var u: Int + try { + u = Integer.parseInt(str) + } catch (e: NumberFormatException) { + u = def + } + + return u + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readUInt(xpp: XmlPullParser, uDefault: Long): Long { + val u: Long = readULong(xpp, uDefault) + + if (u < 0 || u > MAX_UINT) { + throw NumberFormatException("Outside of the uint size") + } + + return u + + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readLong(xpp: XmlPullParser, def: Long): Long { + val str = readString(xpp) + + val u: Long + u = try { + java.lang.Long.parseLong(str) + } catch (e: NumberFormatException) { + def + } + + return u + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readULong(xpp: XmlPullParser, uDefault: Long): Long { + var u = readLong(xpp, uDefault) + + if (u < 0) { + u = uDefault + } + + return u + } + + @Throws(XmlPullParserException::class, IOException::class) + private fun readProtectedString(xpp: XmlPullParser): ProtectedString { + val buf = processNode(xpp) + + if (buf != null) { + try { + return ProtectedString(true, String(buf, Charset.forName("UTF-8"))) + } catch (e: UnsupportedEncodingException) { + e.printStackTrace() + throw IOException(e.localizedMessage) + } + + } + + return ProtectedString(false, readString(xpp)) + } + + @Throws(IOException::class) + private fun createProtectedBinaryFromData(protection: Boolean, data: ByteArray): ProtectedBinary { + return if (data.size > MemUtil.BUFFER_SIZE_BYTES) { + val file = File(streamDir, unusedCacheFileName) + FileOutputStream(file).use { outputStream -> outputStream.write(data) } + ProtectedBinary(protection, file, data.size) + } else { + ProtectedBinary(protection, data) + } + } + + @Throws(XmlPullParserException::class, IOException::class) + private fun readProtectedBinary(xpp: XmlPullParser): ProtectedBinary? { + val ref = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrRef) + if (ref != null) { + xpp.next() // Consume end tag + + val id = Integer.parseInt(ref) + return mDatabase.binPool[id] + } + + var compressed = false + val comp = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrCompressed) + if (comp != null) { + compressed = comp.equals(PwDatabaseV4XML.ValTrue, ignoreCase = true) + } + + val buf = processNode(xpp) + + if (buf != null) { + createProtectedBinaryFromData(true, buf) + } + + val base64 = readString(xpp) + if (base64.isEmpty()) + return ProtectedBinary() + + var data = Base64Coder.decode(base64) + + if (compressed) { + data = MemUtil.decompress(data) + } + + return createProtectedBinaryFromData(false, data) + } + + @Throws(IOException::class, XmlPullParserException::class) + private fun readString(xpp: XmlPullParser): String { + val buf = processNode(xpp) + + if (buf != null) { + try { + return String(buf, Charset.forName("UTF-8")) + } catch (e: UnsupportedEncodingException) { + throw IOException(e.localizedMessage) + } + + } + + //readNextNode = false; + return xpp.nextText() + + } + + @Throws(XmlPullParserException::class, IOException::class) + private fun readStringRaw(xpp: XmlPullParser): String { + + //readNextNode = false; + return xpp.nextText() + } + + @Throws(XmlPullParserException::class, IOException::class) + private fun processNode(xpp: XmlPullParser): ByteArray? { + //(xpp.getEventType() == XmlPullParser.START_TAG); + + var buf: ByteArray? = null + + if (xpp.attributeCount > 0) { + val protect = xpp.getAttributeValue(null, PwDatabaseV4XML.AttrProtected) + if (protect != null && protect.equals(PwDatabaseV4XML.ValTrue, ignoreCase = true)) { + // TODO stream for encrypted data + val encrypted = readStringRaw(xpp) + + if (encrypted.isNotEmpty()) { + buf = Base64Coder.decode(encrypted) + val plainText = ByteArray(buf!!.size) + + randomStream!!.processBytes(buf, 0, buf.size, plainText, 0) + + return plainText + } else { + buf = ByteArray(0) + } + } + } + + return buf + } + + @Throws(XmlPullParserException::class, IOException::class) + private fun switchContext(ctxCurrent: KdbContext, ctxNew: KdbContext, + xpp: XmlPullParser): KdbContext { + + if (xpp.isEmptyElementTag) { + xpp.next() // Consume the end tag + return ctxCurrent + } + return ctxNew + } + + + private fun stringToBoolean(str: String?): Boolean? { + if (str == null || str.isEmpty()) { + return null + } + + val trimmed = str.trim { it <= ' ' } + if (trimmed.equals("true", ignoreCase = true)) { + return true + } else if (trimmed.equals("false", ignoreCase = true)) { + return false + } + + return null + } + + companion object { + + private const val DEFAULT_HISTORY_DAYS: Long = 365 + + @Throws(XmlPullParserException::class) + private fun createPullParser(readerStream: InputStream): XmlPullParser { + val xmlPullParserFactory = XmlPullParserFactory.newInstance() + xmlPullParserFactory.isNamespaceAware = false + + val xpp = xmlPullParserFactory.newPullParser() + xpp.setInput(readerStream, null) + + return xpp + } + + private const val MAX_UINT = 4294967296L // 2^32 + } +} From 93222990b538e90b2e7bddc2a406fdbe696f543d Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 2 Jun 2019 16:10:57 +0200 Subject: [PATCH 118/289] Kotlinized database package --- .../keepass/activities/EntryActivity.java | 3 +- .../keepass/activities/EntryEditActivity.java | 2 +- .../keepass/activities/GroupActivity.java | 2 +- .../{database => crypto}/CrsAlgorithm.kt | 2 +- .../keepass/crypto/PwStreamCipherFactory.java | 2 - .../database/cursor/ExtraFieldCursor.kt | 2 +- .../keepass/database/element/BinaryPool.kt | 2 +- .../keepass/database/element/Database.kt | 10 +- .../database/element/EntryVersioned.kt | 2 +- .../keepass/database/element/ExtraFields.kt | 2 +- .../database/element/PwDatabaseV4.java | 3 +- .../database/element/PwDbHeaderV3.java | 138 ---- .../database/element/PwDbHeaderV4.java | 378 --------- .../keepass/database/element/PwEntryV4.kt | 4 +- .../element/security/ProtectedBinary.kt | 154 ++++ .../element/security/ProtectedString.kt | 78 ++ .../{ => file}/PwCompressionAlgorithm.kt | 2 +- .../database/{element => file}/PwDbHeader.kt | 3 +- .../keepass/database/file/PwDbHeaderV3.kt | 117 +++ .../keepass/database/file/PwDbHeaderV4.kt | 334 ++++++++ .../database/{ => file}/load/Importer.kt | 2 +- .../database/{ => file}/load/ImporterV3.kt | 4 +- .../database/{ => file}/load/ImporterV4.kt | 11 +- .../save/PwDbHeaderOutput.kt} | 10 +- .../database/file/save/PwDbHeaderOutputV3.kt | 64 ++ .../database/file/save/PwDbHeaderOutputV4.kt | 151 ++++ .../file/save/PwDbInnerHeaderOutputV4.kt | 68 ++ .../keepass/database/file/save/PwDbOutput.kt | 52 ++ .../database/file/save/PwDbV3Output.kt | 279 +++++++ .../database/file/save/PwDbV4Output.kt | 743 +++++++++++++++++ .../database/file/save/PwEntryOutputV3.kt | 166 ++++ .../database/file/save/PwGroupOutputV3.kt | 110 +++ .../database/save/PwDbHeaderOutputV3.java | 66 -- .../database/save/PwDbHeaderOutputV4.java | 149 ---- .../save/PwDbInnerHeaderOutputV4.java | 71 -- .../keepass/database/save/PwDbOutput.java | 54 -- .../keepass/database/save/PwDbV3Output.java | 278 ------- .../keepass/database/save/PwDbV4Output.java | 765 ------------------ .../database/save/PwEntryOutputV3.java | 176 ---- .../database/save/PwGroupOutputV3.java | 113 --- .../database/search/EntrySearchHandlerV4.kt | 2 +- .../keepass/database/search/SearchDbHelper.kt | 6 +- .../iterator/EntrySearchStringIterator.kt | 2 +- .../iterator/EntrySearchStringIteratorV3.kt | 2 +- .../iterator/EntrySearchStringIteratorV4.kt | 4 +- .../database/security/ProtectedBinary.java | 158 ---- .../database/security/ProtectedString.java | 89 -- .../keepass/view/EntryContentsView.java | 2 +- .../keepass/view/EntryCustomField.java | 2 +- .../view/EntryCustomFieldProtected.java | 2 +- .../keepass/view/EntryEditCustomField.java | 2 +- 51 files changed, 2362 insertions(+), 2481 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/{database => crypto}/CrsAlgorithm.kt (96%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedString.kt rename app/src/main/java/com/kunzisoft/keepass/database/{ => file}/PwCompressionAlgorithm.kt (96%) rename app/src/main/java/com/kunzisoft/keepass/database/{element => file}/PwDbHeader.kt (95%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV3.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt rename app/src/main/java/com/kunzisoft/keepass/database/{ => file}/load/Importer.kt (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => file}/load/ImporterV3.kt (98%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => file}/load/ImporterV4.kt (99%) rename app/src/main/java/com/kunzisoft/keepass/database/{save/PwDbHeaderOutput.java => file/save/PwDbHeaderOutput.kt} (81%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV3.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/iterator/EntrySearchStringIterator.kt (93%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/iterator/EntrySearchStringIteratorV3.kt (97%) rename app/src/main/java/com/kunzisoft/keepass/database/{ => search}/iterator/EntrySearchStringIteratorV4.kt (95%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedString.java diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 4dc4ad970..4ee2698da 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -42,11 +42,10 @@ import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.element.ExtraFields; import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.element.EntryVersioned; import com.kunzisoft.keepass.database.element.PwNodeId; -import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.notifications.NotificationCopyingService; import com.kunzisoft.keepass.notifications.NotificationField; import com.kunzisoft.keepass.settings.PreferencesUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index 66f53fcd0..30c9f9503 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -38,7 +38,7 @@ import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; import com.kunzisoft.keepass.database.element.*; -import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; import com.kunzisoft.keepass.settings.PreferencesUtil; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 694634a49..e90217a6d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -72,7 +72,7 @@ import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable; import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable; import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; import com.kunzisoft.keepass.database.element.*; -import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.kt b/app/src/main/java/com/kunzisoft/keepass/crypto/CrsAlgorithm.kt similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.kt rename to app/src/main/java/com/kunzisoft/keepass/crypto/CrsAlgorithm.kt index b50c1522d..eb492fb4d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/CrsAlgorithm.kt +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/CrsAlgorithm.kt @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database +package com.kunzisoft.keepass.crypto enum class CrsAlgorithm constructor(val id: Int) { diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java b/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java index 98d369858..d4a8f9866 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java @@ -19,8 +19,6 @@ */ package com.kunzisoft.keepass.crypto; -import com.kunzisoft.keepass.database.CrsAlgorithm; - import org.spongycastle.crypto.StreamCipher; import org.spongycastle.crypto.engines.ChaCha7539Engine; import org.spongycastle.crypto.engines.Salsa20Engine; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt b/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt index a79321a61..33913ab08 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/cursor/ExtraFieldCursor.kt @@ -4,7 +4,7 @@ import android.database.MatrixCursor import android.provider.BaseColumns import com.kunzisoft.keepass.database.element.PwEntryV4 -import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.database.element.security.ProtectedString class ExtraFieldCursor : MatrixCursor(arrayOf( _ID, diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt index 5a90d01e8..3f3757442 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/BinaryPool.kt @@ -19,7 +19,7 @@ */ package com.kunzisoft.keepass.database.element -import com.kunzisoft.keepass.database.security.ProtectedBinary +import com.kunzisoft.keepass.database.element.security.ProtectedBinary import java.util.HashMap import kotlin.collections.Map.Entry diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 86e1561c1..4048ca694 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -31,10 +31,12 @@ import com.kunzisoft.keepass.database.NodeHandler import com.kunzisoft.keepass.database.cursor.EntryCursorV3 import com.kunzisoft.keepass.database.cursor.EntryCursorV4 import com.kunzisoft.keepass.database.exception.* -import com.kunzisoft.keepass.database.load.ImporterV3 -import com.kunzisoft.keepass.database.load.ImporterV4 -import com.kunzisoft.keepass.database.save.PwDbV3Output -import com.kunzisoft.keepass.database.save.PwDbV4Output +import com.kunzisoft.keepass.database.file.PwDbHeaderV3 +import com.kunzisoft.keepass.database.file.PwDbHeaderV4 +import com.kunzisoft.keepass.database.file.load.ImporterV3 +import com.kunzisoft.keepass.database.file.load.ImporterV4 +import com.kunzisoft.keepass.database.file.save.PwDbV3Output +import com.kunzisoft.keepass.database.file.save.PwDbV4Output import com.kunzisoft.keepass.database.search.SearchDbHelper import com.kunzisoft.keepass.icons.IconDrawableFactory import com.kunzisoft.keepass.settings.PreferencesUtil 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 affea85f5..876382e00 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 @@ -2,7 +2,7 @@ package com.kunzisoft.keepass.database.element import android.os.Parcel import android.os.Parcelable -import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.database.element.security.ProtectedString import java.util.* import kotlin.collections.ArrayList diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt index e833d6c6d..cbbec73f1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/ExtraFields.kt @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element import android.os.Parcel import android.os.Parcelable -import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.utils.MemUtil import java.util.HashMap diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java index 5ec388b76..3f20a2821 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.java @@ -28,9 +28,10 @@ import com.kunzisoft.keepass.crypto.engine.CipherEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; -import com.kunzisoft.keepass.database.*; import com.kunzisoft.keepass.database.exception.InvalidKeyFileException; import com.kunzisoft.keepass.database.exception.UnknownKDF; +import com.kunzisoft.keepass.database.file.PwCompressionAlgorithm; + import org.w3c.dom.*; import javax.annotation.Nullable; diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java deleted file mode 100644 index 3f57f5bc7..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV3.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - * - -Derived from - -KeePass for J2ME - - -Copyright 2007 Naomaru Itoi - -This file was derived from - -Java clone of KeePass - A KeePass file viewer for Java -Copyright 2006 Bill Zwicky - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; version 2 - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package com.kunzisoft.keepass.database.element; - -import com.kunzisoft.keepass.stream.LEDataInputStream; - -import java.io.IOException; - -public class PwDbHeaderV3 extends PwDbHeader { - - // DB sig from KeePass 1.03 - public static final int DBSIG_2 = 0xB54BFB65; - // DB sig from KeePass 1.03 - public static final int DBVER_DW = 0x00030003; - - public static final int FLAG_SHA2 = 1; - public static final int FLAG_RIJNDAEL = 2; - public static final int FLAG_ARCFOUR = 4; - public static final int FLAG_TWOFISH = 8; - - /** Size of byte buffer needed to hold this struct. */ - public static final int BUF_SIZE = 124; - - /** - * Used for the dwKeyEncRounds AES transformations - */ - public byte[] transformSeed = new byte[32]; - - public int signature1; // = PWM_DBSIG_1 - public int signature2; // = DBSIG_2 - public int flags; - public int version; - - /** Number of groups in the database */ - public int numGroups; - /** Number of entries in the database */ - public int numEntries; - - /** - * SHA-256 hash of the database, used for integrity check - */ - public byte[] contentsHash = new byte[32]; - - public int numKeyEncRounds; - - /** - * Parse given buf, as read from file. - * @param buf - * @throws IOException - */ - public void loadFromFile(byte[] buf, int offset ) throws IOException { - signature1 = LEDataInputStream.readInt( buf, offset); - signature2 = LEDataInputStream.readInt( buf, offset + 4 ); - flags = LEDataInputStream.readInt( buf, offset + 8 ); - version = LEDataInputStream.readInt( buf, offset + 12 ); - - System.arraycopy( buf, offset + 16, getMasterSeed(), 0, 16 ); - System.arraycopy( buf, offset + 32, getEncryptionIV(), 0, 16 ); - - numGroups = LEDataInputStream.readInt( buf, offset + 48 ); - numEntries = LEDataInputStream.readInt( buf, offset + 52 ); - - System.arraycopy( buf, offset + 56, contentsHash, 0, 32 ); - - System.arraycopy( buf, offset + 88, transformSeed, 0, 32 ); - numKeyEncRounds = LEDataInputStream.readInt( buf, offset + 120 ); - if ( numKeyEncRounds < 0 ) { - // TODO: Really treat this like an unsigned integer - throw new IOException("Does not support more than " + Integer.MAX_VALUE + " rounds."); - } - } - - public PwDbHeaderV3() { - setMasterSeed(new byte[16]); - } - - public static boolean matchesHeader(int sig1, int sig2) { - return (sig1 == PwDbHeader.PWM_DBSIG_1) && (sig2 == DBSIG_2); - } - - - /** Determine if the database version is compatible with this application - * @return true, if it is compatible - */ - public boolean matchesVersion() { - return compatibleHeaders(version, DBVER_DW); - } - - public static boolean compatibleHeaders(int one, int two) { - return (one & 0xFFFFFF00) == (two & 0xFFFFFF00); - } - - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java deleted file mode 100644 index e60a8b26a..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeaderV4.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf; -import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; -import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; -import com.kunzisoft.keepass.database.CrsAlgorithm; -import com.kunzisoft.keepass.database.NodeHandler; -import com.kunzisoft.keepass.database.PwCompressionAlgorithm; -import com.kunzisoft.keepass.database.exception.InvalidDBVersionException; -import com.kunzisoft.keepass.stream.CopyInputStream; -import com.kunzisoft.keepass.stream.HmacBlockStream; -import com.kunzisoft.keepass.stream.LEDataInputStream; -import com.kunzisoft.keepass.utils.Types; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.DigestInputStream; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class PwDbHeaderV4 extends PwDbHeader { - public static final int DBSIG_PRE2 = 0xB54BFB66; - public static final int DBSIG_2 = 0xB54BFB67; - - private static final int FILE_VERSION_CRITICAL_MASK = 0xFFFF0000; - public static final int FILE_VERSION_32_3 = 0x00030001; - public static final int FILE_VERSION_32_4 = 0x00040000; - - public class PwDbHeaderV4Fields { - public static final byte EndOfHeader = 0; - public static final byte Comment = 1; - public static final byte CipherID = 2; - public static final byte CompressionFlags = 3; - public static final byte MasterSeed = 4; - public static final byte TransformSeed = 5; - public static final byte TransformRounds = 6; - public static final byte EncryptionIV = 7; - public static final byte InnerRandomstreamKey = 8; - public static final byte StreamStartBytes = 9; - public static final byte InnerRandomStreamID = 10; - public static final byte KdfParameters = 11; - public static final byte PublicCustomData = 12; - } - - public class PwDbInnerHeaderV4Fields { - public static final byte EndOfHeader = 0; - public static final byte InnerRandomStreamID = 1; - public static final byte InnerRandomstreamKey = 2; - public static final byte Binary = 3; - } - - public class KdbxBinaryFlags { - public static final byte None = 0; - public static final byte Protected = 1; - } - - public class HeaderAndHash { - public byte[] header; - public byte[] hash; - - public HeaderAndHash (byte[] header, byte[] hash) { - this.header = header; - this.hash = hash; - } - } - - private PwDatabaseV4 db; - public byte[] innerRandomStreamKey = new byte[32]; - public byte[] streamStartBytes = new byte[32]; - public CrsAlgorithm innerRandomStream; - public long version; - - public PwDbHeaderV4(PwDatabaseV4 databaseV4) { - this.db = databaseV4; - this.version = getMinKdbxVersion(databaseV4); // Only for writing - this.setMasterSeed(new byte[32]); - } - - public long getVersion() { - return version; - } - - public void setVersion(long version) { - this.version = version; - } - - private class GroupHasCustomData extends NodeHandler { - - boolean hasCustomData = false; - - @Override - public boolean operate(PwGroupV4 node) { - if (node == null) { - return true; - } - if (node.containsCustomData()) { - hasCustomData = true; - return false; - } - - return true; - } - } - - private class EntryHasCustomData extends NodeHandler { - - boolean hasCustomData = false; - - @Override - public boolean operate(PwEntryV4 entry) { - if (entry == null) { - return true; - } - - if (entry.containsCustomData()) { - hasCustomData = true; - return false; - } - - return true; - } - } - - private int getMinKdbxVersion(PwDatabaseV4 databaseV4) { - // Return v4 if AES is not use - if (databaseV4.getKdfParameters() != null - && !databaseV4.getKdfParameters().getUUID().equals(AesKdf.CIPHER_UUID)) { - return PwDbHeaderV4.FILE_VERSION_32_4; - } - - // Return V4 if custom data are present - if (databaseV4.containsPublicCustomData()) { - return PwDbHeaderV4.FILE_VERSION_32_4; - } - - EntryHasCustomData entryHandler = new EntryHasCustomData(); - GroupHasCustomData groupHandler = new GroupHasCustomData(); - - if (databaseV4.getRootGroup() == null ) { - return PwDbHeaderV4.FILE_VERSION_32_3; - } - databaseV4.getRootGroup().doForEachChildAndForIt(entryHandler, groupHandler); - if (groupHandler.hasCustomData || entryHandler.hasCustomData) { - return PwDbHeaderV4.FILE_VERSION_32_4; - } - - return PwDbHeaderV4.FILE_VERSION_32_3; - } - - /** Assumes the input stream is at the beginning of the .kdbx file - * @param is - * @throws IOException - * @throws InvalidDBVersionException - */ - public HeaderAndHash loadFromFile(InputStream is) throws IOException, InvalidDBVersionException { - MessageDigest md; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IOException("No SHA-256 implementation"); - } - - ByteArrayOutputStream headerBOS = new ByteArrayOutputStream(); - CopyInputStream cis = new CopyInputStream(is, headerBOS); - DigestInputStream dis = new DigestInputStream(cis, md); - LEDataInputStream lis = new LEDataInputStream(dis); - - int sig1 = lis.readInt(); - int sig2 = lis.readInt(); - - if ( ! matchesHeader(sig1, sig2) ) { - throw new InvalidDBVersionException(); - } - - version = lis.readUInt(); // Erase previous value - if ( ! validVersion(version) ) { - throw new InvalidDBVersionException(); - } - - boolean done = false; - while ( ! done ) { - done = readHeaderField(lis); - } - - byte[] hash = md.digest(); - return new HeaderAndHash(headerBOS.toByteArray(), hash); - } - - private boolean readHeaderField(LEDataInputStream dis) throws IOException { - byte fieldID = (byte) dis.read(); - - int fieldSize; - if (version < FILE_VERSION_32_4) { - fieldSize = dis.readUShort(); - } else { - fieldSize = dis.readInt(); - } - - byte[] fieldData = null; - if ( fieldSize > 0 ) { - fieldData = new byte[fieldSize]; - - int readSize = dis.read(fieldData); - if ( readSize != fieldSize ) { - throw new IOException("Header ended early."); - } - } - - switch ( fieldID ) { - case PwDbHeaderV4Fields.EndOfHeader: - return true; - - case PwDbHeaderV4Fields.CipherID: - setCipher(fieldData); - break; - - case PwDbHeaderV4Fields.CompressionFlags: - setCompressionFlags(fieldData); - break; - - case PwDbHeaderV4Fields.MasterSeed: - setMasterSeed(fieldData); - break; - - case PwDbHeaderV4Fields.TransformSeed: - if(version < PwDbHeaderV4.FILE_VERSION_32_4) - setTransformSeed(fieldData); - break; - - case PwDbHeaderV4Fields.TransformRounds: - if(version < PwDbHeaderV4.FILE_VERSION_32_4) - setTransformRound(fieldData); - break; - - case PwDbHeaderV4Fields.EncryptionIV: - setEncryptionIV(fieldData); - break; - - case PwDbHeaderV4Fields.InnerRandomstreamKey: - if(version < PwDbHeaderV4.FILE_VERSION_32_4) - innerRandomStreamKey = fieldData; - break; - - case PwDbHeaderV4Fields.StreamStartBytes: - streamStartBytes = fieldData; - break; - - case PwDbHeaderV4Fields.InnerRandomStreamID: - if(version < PwDbHeaderV4.FILE_VERSION_32_4) - setRandomStreamID(fieldData); - break; - - case PwDbHeaderV4Fields.KdfParameters: - db.setKdfParameters(KdfParameters.deserialize(fieldData)); - break; - - case PwDbHeaderV4Fields.PublicCustomData: - db.setPublicCustomData(KdfParameters.deserialize(fieldData)); // TODO verify - default: - throw new IOException("Invalid header type: " + fieldID); - - } - - return false; - } - - private void assignAesKdfEngineIfNotExists() { - if (db.getKdfParameters() == null || !db.getKdfParameters().getUUID().equals(KdfFactory.aesKdf.getUUID())) { - db.setKdfParameters(KdfFactory.aesKdf.getDefaultParameters()); - } - } - - private void setCipher(byte[] pbId) throws IOException { - if ( pbId == null || pbId.length != 16 ) { - throw new IOException("Invalid cipher ID."); - } - - db.setDataCipher(Types.bytestoUUID(pbId)); - } - - private void setTransformSeed(byte[] seed) { - assignAesKdfEngineIfNotExists(); - db.getKdfParameters().setByteArray(AesKdf.ParamSeed, seed); - } - - private void setTransformRound(byte[] roundsByte) { - assignAesKdfEngineIfNotExists(); - long rounds = LEDataInputStream.readLong(roundsByte, 0); - db.getKdfParameters().setUInt64(AesKdf.ParamRounds, rounds); - db.setNumberKeyEncryptionRounds(rounds); - } - - private void setCompressionFlags(byte[] pbFlags) throws IOException { - if ( pbFlags == null || pbFlags.length != 4 ) { - throw new IOException("Invalid compression flags."); - } - - int flag = LEDataInputStream.readInt(pbFlags, 0); - if ( flag < 0 || flag >= PwCompressionAlgorithm.values().length ) { - throw new IOException("Unrecognized compression flag."); - } - - db.setCompressionAlgorithm(PwCompressionAlgorithm.Companion.fromId(flag)); - } - - public void setRandomStreamID(byte[] streamID) throws IOException { - if ( streamID == null || streamID.length != 4 ) { - throw new IOException("Invalid stream id."); - } - - int id = LEDataInputStream.readInt(streamID, 0); - if ( id < 0 || id >= CrsAlgorithm.values().length) { - throw new IOException("Invalid stream id."); - } - - innerRandomStream = CrsAlgorithm.Companion.fromId(id); - } - - /** - * Determines if this is a supported version. - * - * A long is needed here to represent the unsigned int since we perform arithmetic on it. - * @param version Database version - * @return true if it's a supported version - */ - private boolean validVersion(long version) { - return ! ((version & FILE_VERSION_CRITICAL_MASK) > (FILE_VERSION_32_4 & FILE_VERSION_CRITICAL_MASK)); - } - - public static boolean matchesHeader(int sig1, int sig2) { - return (sig1 == PwDbHeader.PWM_DBSIG_1) && ( (sig2 == DBSIG_PRE2) || (sig2 == DBSIG_2) ); - } - - public static byte[] computeHeaderHmac(byte[] header, byte[] key) throws IOException{ - byte[] blockKey = HmacBlockStream.GetHmacKey64(key, Types.ULONG_MAX_VALUE); - - Mac hmac; - try { - hmac = Mac.getInstance("HmacSHA256"); - SecretKeySpec signingKey = new SecretKeySpec(blockKey, "HmacSHA256"); - hmac.init(signingKey); - } catch (NoSuchAlgorithmException e) { - throw new IOException("No HmacAlogirthm"); - } catch (InvalidKeyException e) { - throw new IOException("Invalid Hmac Key"); - } - - return hmac.doFinal(header); - } - - public byte[] getTransformSeed() { - // version < FILE_VERSION_32_4) - return db.getKdfParameters().getByteArray(AesKdf.ParamSeed); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt index 7eacb931f..663cff5b8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt @@ -21,8 +21,8 @@ package com.kunzisoft.keepass.database.element import android.os.Parcel import android.os.Parcelable -import com.kunzisoft.keepass.database.security.ProtectedBinary -import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.database.element.security.ProtectedBinary +import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.utils.MemUtil import com.kunzisoft.keepass.utils.SprEngineV4 import java.util.* diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt new file mode 100644 index 000000000..bd229647c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedBinary.kt @@ -0,0 +1,154 @@ +/* + * Copyright 2018 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element.security + +import android.os.Parcel +import android.os.Parcelable +import android.util.Log + +import java.io.ByteArrayInputStream +import java.io.File +import java.io.FileInputStream +import java.io.IOException +import java.io.InputStream +import java.util.Arrays + +class ProtectedBinary : Parcelable { + + var isProtected: Boolean = false + private set + private var data: ByteArray? = null + private var dataFile: File? = null + private var size: Int = 0 + + fun length(): Long { + if (data != null) + return data!!.size.toLong() + return if (dataFile != null) size.toLong() else 0 + } + + /** + * Empty protected binary + */ + constructor() { + this.isProtected = false + this.data = null + this.dataFile = null + this.size = 0 + } + + constructor(protectedBinary: ProtectedBinary) { + this.isProtected = protectedBinary.isProtected + this.data = protectedBinary.data + this.dataFile = protectedBinary.dataFile + this.size = protectedBinary.size + } + + constructor(enableProtection: Boolean, data: ByteArray?) { + this.isProtected = enableProtection + this.data = data + this.dataFile = null + if (data != null) + this.size = data.size + else + this.size = 0 + } + + constructor(enableProtection: Boolean, dataFile: File, size: Int) { + this.isProtected = enableProtection + this.data = null + this.dataFile = dataFile + this.size = size + } + + private constructor(parcel: Parcel) { + isProtected = parcel.readByte().toInt() != 0 + parcel.readByteArray(data) + dataFile = File(parcel.readString()) + size = parcel.readInt() + } + + @Throws(IOException::class) + fun getData(): InputStream? { + return when { + data != null -> ByteArrayInputStream(data) + dataFile != null -> FileInputStream(dataFile!!) + else -> null + } + } + + fun clear() { + data = null + if (dataFile != null && !dataFile!!.delete()) + Log.e(TAG, "Unable to delete temp file " + dataFile!!.absolutePath) + } + + override fun equals(other: Any?): Boolean { + if (this === other) + return true + if (other == null || javaClass != other.javaClass) + return false + if (other !is ProtectedBinary) + return false + return isProtected == other.isProtected && + size == other.size && + Arrays.equals(data, other.data) && + dataFile != null && + dataFile == other.dataFile + } + + override fun hashCode(): Int { + + var result = 0 + result = 31 * result + if (isProtected) 1 else 0 + result = 31 * result + dataFile!!.hashCode() + result = 31 * result + Integer.valueOf(size)!!.hashCode() + result = 31 * result + Arrays.hashCode(data) + return result + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeByte((if (isProtected) 1 else 0).toByte()) + dest.writeByteArray(data) + dest.writeString(dataFile!!.absolutePath) + dest.writeInt(size) + } + + companion object { + + private val TAG = ProtectedBinary::class.java.name + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): ProtectedBinary { + return ProtectedBinary(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedString.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedString.kt new file mode 100644 index 000000000..fdf4c33c8 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/security/ProtectedString.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element.security + +import android.os.Parcel +import android.os.Parcelable + +class ProtectedString : Parcelable { + + var isProtected: Boolean = false + private set + private var string: String = "" + + constructor(toCopy: ProtectedString) { + this.isProtected = toCopy.isProtected + this.string = toCopy.string + } + + @JvmOverloads + constructor(enableProtection: Boolean = false, string: String = "") { + this.isProtected = enableProtection + this.string = string + } + + constructor(parcel: Parcel) { + isProtected = parcel.readByte().toInt() != 0 + string = parcel.readString() + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeByte((if (isProtected) 1 else 0).toByte()) + dest.writeString(string) + } + + fun length(): Int { + return string.length + } + + override fun toString(): String { + return string + } + + companion object { + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): ProtectedString { + return ProtectedString(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/PwCompressionAlgorithm.kt similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.kt rename to app/src/main/java/com/kunzisoft/keepass/database/file/PwCompressionAlgorithm.kt index a292d5e90..a560e5c0b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/PwCompressionAlgorithm.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/PwCompressionAlgorithm.kt @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database +package com.kunzisoft.keepass.database.file // Note: We can get away with using int's to store unsigned 32-bit ints // since we won't do arithmetic on these values (also unlikely to diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeader.kt similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.kt rename to app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeader.kt index 0a2148c1e..966bbce60 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDbHeader.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeader.kt @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.element +package com.kunzisoft.keepass.database.file abstract class PwDbHeader { @@ -32,7 +32,6 @@ abstract class PwDbHeader { var encryptionIV = ByteArray(16) companion object { - const val PWM_DBSIG_1 = -0x655d26fd } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV3.kt new file mode 100644 index 000000000..3d856da20 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV3.kt @@ -0,0 +1,117 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. Derived from KeePass for J2ME + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + * + */ + +package com.kunzisoft.keepass.database.file + +import com.kunzisoft.keepass.stream.LEDataInputStream + +import java.io.IOException + +class PwDbHeaderV3 : PwDbHeader() { + + /** + * Used for the dwKeyEncRounds AES transformations + */ + var transformSeed = ByteArray(32) + + var signature1: Int = 0 // = PWM_DBSIG_1 + var signature2: Int = 0 // = DBSIG_2 + var flags: Int = 0 + var version: Int = 0 + + /** Number of groups in the database */ + var numGroups: Int = 0 + /** Number of entries in the database */ + var numEntries: Int = 0 + + /** + * SHA-256 hash of the database, used for integrity check + */ + var contentsHash = ByteArray(32) + + var numKeyEncRounds: Int = 0 + + /** + * Parse given buf, as read from file. + * @param buf + * @throws IOException + */ + @Throws(IOException::class) + fun loadFromFile(buf: ByteArray, offset: Int) { + signature1 = LEDataInputStream.readInt(buf, offset) + signature2 = LEDataInputStream.readInt(buf, offset + 4) + flags = LEDataInputStream.readInt(buf, offset + 8) + version = LEDataInputStream.readInt(buf, offset + 12) + + System.arraycopy(buf, offset + 16, masterSeed!!, 0, 16) + System.arraycopy(buf, offset + 32, encryptionIV, 0, 16) + + numGroups = LEDataInputStream.readInt(buf, offset + 48) + numEntries = LEDataInputStream.readInt(buf, offset + 52) + + System.arraycopy(buf, offset + 56, contentsHash, 0, 32) + + System.arraycopy(buf, offset + 88, transformSeed, 0, 32) + numKeyEncRounds = LEDataInputStream.readInt(buf, offset + 120) + if (numKeyEncRounds < 0) { + // TODO: Really treat this like an unsigned integer + throw IOException("Does not support more than " + Integer.MAX_VALUE + " rounds.") + } + } + + init { + masterSeed = ByteArray(16) + } + + + /** Determine if the database version is compatible with this application + * @return true, if it is compatible + */ + fun matchesVersion(): Boolean { + return compatibleHeaders(version, DBVER_DW) + } + + companion object { + + // DB sig from KeePass 1.03 + const val DBSIG_2 = -0x4ab4049b + // DB sig from KeePass 1.03 + const val DBVER_DW = 0x00030003 + + const val FLAG_SHA2 = 1 + const val FLAG_RIJNDAEL = 2 + const val FLAG_ARCFOUR = 4 + const val FLAG_TWOFISH = 8 + + /** Size of byte buffer needed to hold this struct. */ + const val BUF_SIZE = 124 + + fun matchesHeader(sig1: Int, sig2: Int): Boolean { + return sig1 == PWM_DBSIG_1 && sig2 == DBSIG_2 + } + + fun compatibleHeaders(one: Int, two: Int): Boolean { + return one and -0x100 == two and -0x100 + } + } + + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt new file mode 100644 index 000000000..0de6566dc --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt @@ -0,0 +1,334 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file + +import com.kunzisoft.keepass.crypto.keyDerivation.AesKdf +import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory +import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters +import com.kunzisoft.keepass.crypto.CrsAlgorithm +import com.kunzisoft.keepass.database.NodeHandler +import com.kunzisoft.keepass.database.element.PwDatabaseV4 +import com.kunzisoft.keepass.database.element.PwEntryV4 +import com.kunzisoft.keepass.database.element.PwGroupV4 +import com.kunzisoft.keepass.database.exception.InvalidDBVersionException +import com.kunzisoft.keepass.stream.CopyInputStream +import com.kunzisoft.keepass.stream.HmacBlockStream +import com.kunzisoft.keepass.stream.LEDataInputStream +import com.kunzisoft.keepass.utils.Types + +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.security.DigestInputStream +import java.security.InvalidKeyException +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException + +class PwDbHeaderV4(private val db: PwDatabaseV4) : PwDbHeader() { + var innerRandomStreamKey: ByteArray = ByteArray(32) + var streamStartBytes: ByteArray = ByteArray(32) + var innerRandomStream: CrsAlgorithm? = null + var version: Long = 0 + + // version < FILE_VERSION_32_4) + var transformSeed: ByteArray? + get() = db.kdfParameters.getByteArray(AesKdf.ParamSeed) + private set(seed) { + assignAesKdfEngineIfNotExists() + db.kdfParameters.setByteArray(AesKdf.ParamSeed, seed) + } + + object PwDbHeaderV4Fields { + const val EndOfHeader: Byte = 0 + const val Comment: Byte = 1 + const val CipherID: Byte = 2 + const val CompressionFlags: Byte = 3 + const val MasterSeed: Byte = 4 + const val TransformSeed: Byte = 5 + const val TransformRounds: Byte = 6 + const val EncryptionIV: Byte = 7 + const val InnerRandomstreamKey: Byte = 8 + const val StreamStartBytes: Byte = 9 + const val InnerRandomStreamID: Byte = 10 + const val KdfParameters: Byte = 11 + const val PublicCustomData: Byte = 12 + } + + object PwDbInnerHeaderV4Fields { + const val EndOfHeader: Byte = 0 + const val InnerRandomStreamID: Byte = 1 + const val InnerRandomstreamKey: Byte = 2 + const val Binary: Byte = 3 + } + + object KdbxBinaryFlags { + const val None: Byte = 0 + const val Protected: Byte = 1 + } + + inner class HeaderAndHash(var header: ByteArray, var hash: ByteArray) + + init { + this.version = getMinKdbxVersion(db).toLong() // Only for writing + this.masterSeed = ByteArray(32) + } + + private inner class GroupHasCustomData : NodeHandler() { + + internal var hasCustomData = false + + override fun operate(node: PwGroupV4): Boolean { + if (node.containsCustomData()) { + hasCustomData = true + return false + } + return true + } + } + + private inner class EntryHasCustomData : NodeHandler() { + + internal var hasCustomData = false + + override fun operate(node: PwEntryV4): Boolean { + if (node.containsCustomData()) { + hasCustomData = true + return false + } + return true + } + } + + private fun getMinKdbxVersion(databaseV4: PwDatabaseV4): Long { + // Return v4 if AES is not use + if (databaseV4.kdfParameters != null && databaseV4.kdfParameters.uuid != AesKdf.CIPHER_UUID) { + return FILE_VERSION_32_4 + } + + // Return V4 if custom data are present + if (databaseV4.containsPublicCustomData()) { + return FILE_VERSION_32_4 + } + + val entryHandler = EntryHasCustomData() + val groupHandler = GroupHasCustomData() + + if (databaseV4.rootGroup == null) { + return FILE_VERSION_32_3 + } + databaseV4.rootGroup.doForEachChildAndForIt(entryHandler, groupHandler) + return if (groupHandler.hasCustomData || entryHandler.hasCustomData) { + FILE_VERSION_32_4 + } else FILE_VERSION_32_3 + + } + + /** Assumes the input stream is at the beginning of the .kdbx file + * @param `is` + * @throws IOException + * @throws InvalidDBVersionException + */ + @Throws(IOException::class, InvalidDBVersionException::class) + fun loadFromFile(inputStream: InputStream): HeaderAndHash { + val md: MessageDigest + try { + md = MessageDigest.getInstance("SHA-256") + } catch (e: NoSuchAlgorithmException) { + throw IOException("No SHA-256 implementation") + } + + val headerBOS = ByteArrayOutputStream() + val cis = CopyInputStream(inputStream, headerBOS) + val dis = DigestInputStream(cis, md) + val lis = LEDataInputStream(dis) + + val sig1 = lis.readInt() + val sig2 = lis.readInt() + + if (!matchesHeader(sig1, sig2)) { + throw InvalidDBVersionException() + } + + version = lis.readUInt() // Erase previous value + if (!validVersion(version)) { + throw InvalidDBVersionException() + } + + var done = false + while (!done) { + done = readHeaderField(lis) + } + + val hash = md.digest() + return HeaderAndHash(headerBOS.toByteArray(), hash) + } + + @Throws(IOException::class) + private fun readHeaderField(dis: LEDataInputStream): Boolean { + val fieldID = dis.read().toByte() + + val fieldSize: Int = if (version < FILE_VERSION_32_4) { + dis.readUShort() + } else { + dis.readInt() + } + + var fieldData: ByteArray? = null + if (fieldSize > 0) { + fieldData = ByteArray(fieldSize) + + val readSize = dis.read(fieldData) + if (readSize != fieldSize) { + throw IOException("Header ended early.") + } + } + + if (fieldData != null) + when (fieldID) { + PwDbHeaderV4Fields.EndOfHeader -> return true + + PwDbHeaderV4Fields.CipherID -> setCipher(fieldData) + + PwDbHeaderV4Fields.CompressionFlags -> setCompressionFlags(fieldData) + + PwDbHeaderV4Fields.MasterSeed -> masterSeed = fieldData + + PwDbHeaderV4Fields.TransformSeed -> if (version < FILE_VERSION_32_4) + transformSeed = fieldData + + PwDbHeaderV4Fields.TransformRounds -> if (version < FILE_VERSION_32_4) + setTransformRound(fieldData) + + PwDbHeaderV4Fields.EncryptionIV -> encryptionIV = fieldData + + PwDbHeaderV4Fields.InnerRandomstreamKey -> if (version < FILE_VERSION_32_4) + innerRandomStreamKey = fieldData + + PwDbHeaderV4Fields.StreamStartBytes -> streamStartBytes = fieldData + + PwDbHeaderV4Fields.InnerRandomStreamID -> if (version < FILE_VERSION_32_4) + setRandomStreamID(fieldData) + + PwDbHeaderV4Fields.KdfParameters -> db.kdfParameters = KdfParameters.deserialize(fieldData) + + PwDbHeaderV4Fields.PublicCustomData -> { + db.publicCustomData = KdfParameters.deserialize(fieldData) // TODO verify + throw IOException("Invalid header type: $fieldID") + } + else -> throw IOException("Invalid header type: $fieldID") + } + + return false + } + + private fun assignAesKdfEngineIfNotExists() { + if (db.kdfParameters == null || db.kdfParameters.uuid != KdfFactory.aesKdf.uuid) { + db.kdfParameters = KdfFactory.aesKdf.defaultParameters + } + } + + @Throws(IOException::class) + private fun setCipher(pbId: ByteArray?) { + if (pbId == null || pbId.size != 16) { + throw IOException("Invalid cipher ID.") + } + + db.dataCipher = Types.bytestoUUID(pbId) + } + + private fun setTransformRound(roundsByte: ByteArray?) { + assignAesKdfEngineIfNotExists() + val rounds = LEDataInputStream.readLong(roundsByte!!, 0) + db.kdfParameters.setUInt64(AesKdf.ParamRounds, rounds) + db.numberKeyEncryptionRounds = rounds + } + + @Throws(IOException::class) + private fun setCompressionFlags(pbFlags: ByteArray?) { + if (pbFlags == null || pbFlags.size != 4) { + throw IOException("Invalid compression flags.") + } + + val flag = LEDataInputStream.readInt(pbFlags, 0) + if (flag < 0 || flag >= PwCompressionAlgorithm.values().size) { + throw IOException("Unrecognized compression flag.") + } + + db.compressionAlgorithm = PwCompressionAlgorithm.fromId(flag) + } + + @Throws(IOException::class) + fun setRandomStreamID(streamID: ByteArray?) { + if (streamID == null || streamID.size != 4) { + throw IOException("Invalid stream id.") + } + + val id = LEDataInputStream.readInt(streamID, 0) + if (id < 0 || id >= CrsAlgorithm.values().size) { + throw IOException("Invalid stream id.") + } + + innerRandomStream = CrsAlgorithm.fromId(id) + } + + /** + * Determines if this is a supported version. + * + * A long is needed here to represent the unsigned int since we perform arithmetic on it. + * @param version Database version + * @return true if it's a supported version + */ + private fun validVersion(version: Long): Boolean { + return version and FILE_VERSION_CRITICAL_MASK <= FILE_VERSION_32_4 and FILE_VERSION_CRITICAL_MASK + } + + companion object { + const val DBSIG_PRE2 = -0x4ab4049a + const val DBSIG_2 = -0x4ab40499 + + private const val FILE_VERSION_CRITICAL_MASK: Long = -0x10000 + const val FILE_VERSION_32_3: Long = 0x00030001 + const val FILE_VERSION_32_4: Long = 0x00040000 + + fun matchesHeader(sig1: Int, sig2: Int): Boolean { + return sig1 == PWM_DBSIG_1 && (sig2 == DBSIG_PRE2 || sig2 == DBSIG_2) + } + + @Throws(IOException::class) + fun computeHeaderHmac(header: ByteArray, key: ByteArray): ByteArray { + val blockKey = HmacBlockStream.GetHmacKey64(key, Types.ULONG_MAX_VALUE) + + val hmac: Mac + try { + hmac = Mac.getInstance("HmacSHA256") + val signingKey = SecretKeySpec(blockKey, "HmacSHA256") + hmac.init(signingKey) + } catch (e: NoSuchAlgorithmException) { + throw IOException("No HmacAlogirthm") + } catch (e: InvalidKeyException) { + throw IOException("Invalid Hmac Key") + } + + return hmac.doFinal(header) + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/Importer.kt similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt rename to app/src/main/java/com/kunzisoft/keepass/database/file/load/Importer.kt index 5f8f1866a..ccd24df4c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/Importer.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/Importer.kt @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.load +package com.kunzisoft.keepass.database.file.load import com.kunzisoft.keepass.database.element.PwDatabase import com.kunzisoft.keepass.database.exception.InvalidDBException diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.kt rename to app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt index 074bc88e7..18385e01f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt @@ -43,13 +43,15 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -package com.kunzisoft.keepass.database.load +package com.kunzisoft.keepass.database.file.load import android.util.Log import com.kunzisoft.keepass.R import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.exception.* +import com.kunzisoft.keepass.database.file.PwDbHeader +import com.kunzisoft.keepass.database.file.PwDbHeaderV3 import com.kunzisoft.keepass.stream.LEDataInputStream import com.kunzisoft.keepass.stream.NullOutputStream import com.kunzisoft.keepass.tasks.ProgressTaskUpdater diff --git a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt similarity index 99% rename from app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.kt rename to app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt index c5b7a65f1..e415a6c6f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/load/ImporterV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt @@ -17,20 +17,21 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.load +package com.kunzisoft.keepass.database.file.load import biz.source_code.base64Coder.Base64Coder import com.kunzisoft.keepass.R import com.kunzisoft.keepass.crypto.CipherFactory import com.kunzisoft.keepass.crypto.PwStreamCipherFactory import com.kunzisoft.keepass.crypto.engine.CipherEngine -import com.kunzisoft.keepass.database.PwCompressionAlgorithm +import com.kunzisoft.keepass.database.file.PwCompressionAlgorithm import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.exception.ArcFourException import com.kunzisoft.keepass.database.exception.InvalidDBException import com.kunzisoft.keepass.database.exception.InvalidPasswordException -import com.kunzisoft.keepass.database.security.ProtectedBinary -import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.database.file.PwDbHeaderV4 +import com.kunzisoft.keepass.database.element.security.ProtectedBinary +import com.kunzisoft.keepass.database.element.security.ProtectedString import com.kunzisoft.keepass.stream.BetterCipherInputStream import com.kunzisoft.keepass.stream.HashedBlockInputStream import com.kunzisoft.keepass.stream.HmacBlockInputStream @@ -101,7 +102,7 @@ class ImporterV4(private val streamDir: File) : Importer() { mDatabase.binPool.clear() val hh = header.loadFromFile(databaseInputStream) - version = header.getVersion() + version = header.version hashOfHeader = hh.hash val pbHeader = hh.header diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutput.java b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutput.kt similarity index 81% rename from app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutput.java rename to app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutput.kt index 3e1b76ca3..c72a3fba0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutput.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutput.kt @@ -17,11 +17,9 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.save; +package com.kunzisoft.keepass.database.file.save -public class PwDbHeaderOutput { - protected byte[] hashOfHeader = null; - - public byte[] getHashOfHeader() { return hashOfHeader; } - +open class PwDbHeaderOutput { + var hashOfHeader: ByteArray? = null + protected set } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV3.kt new file mode 100644 index 000000000..19f31b503 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV3.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import com.kunzisoft.keepass.database.file.PwDbHeaderV3 +import com.kunzisoft.keepass.stream.LEDataOutputStream + +import java.io.IOException +import java.io.OutputStream + +class PwDbHeaderOutputV3(private val mHeader: PwDbHeaderV3, private val mOS: OutputStream) { + + @Throws(IOException::class) + fun outputStart() { + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.signature1)) + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.signature2)) + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.flags)) + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.version)) + mOS.write(mHeader.masterSeed!!) + mOS.write(mHeader.encryptionIV) + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numGroups)) + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numEntries)) + } + + @Throws(IOException::class) + fun outputContentHash() { + mOS.write(mHeader.contentsHash) + } + + @Throws(IOException::class) + fun outputEnd() { + mOS.write(mHeader.transformSeed) + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numKeyEncRounds)) + } + + @Throws(IOException::class) + fun output() { + outputStart() + outputContentHash() + outputEnd() + } + + @Throws(IOException::class) + fun close() { + mOS.close() + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt new file mode 100644 index 000000000..8d90bee4b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt @@ -0,0 +1,151 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import com.kunzisoft.keepass.collections.VariantDictionary +import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters +import com.kunzisoft.keepass.database.element.PwDatabaseV4 +import com.kunzisoft.keepass.database.file.PwDbHeader +import com.kunzisoft.keepass.database.file.PwDbHeaderV4 +import com.kunzisoft.keepass.database.exception.PwDbOutputException +import com.kunzisoft.keepass.stream.HmacBlockStream +import com.kunzisoft.keepass.stream.LEDataOutputStream +import com.kunzisoft.keepass.stream.MacOutputStream +import com.kunzisoft.keepass.utils.Types + +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.OutputStream +import java.security.DigestOutputStream +import java.security.InvalidKeyException +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException + +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +class PwDbHeaderOutputV4 @Throws(PwDbOutputException::class) +constructor(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os: OutputStream) : PwDbHeaderOutput() { + private val los: LEDataOutputStream + private val mos: MacOutputStream + private val dos: DigestOutputStream + lateinit var headerHmac: ByteArray + + init { + + val md: MessageDigest + try { + md = MessageDigest.getInstance("SHA-256") + } catch (e: NoSuchAlgorithmException) { + throw PwDbOutputException("SHA-256 not implemented here.") + } + + try { + db.makeFinalKey(header.masterSeed) + } catch (e: IOException) { + throw PwDbOutputException(e) + } + + val hmac: Mac + try { + hmac = Mac.getInstance("HmacSHA256") + val signingKey = SecretKeySpec(HmacBlockStream.GetHmacKey64(db.hmacKey, Types.ULONG_MAX_VALUE), "HmacSHA256") + hmac.init(signingKey) + } catch (e: NoSuchAlgorithmException) { + throw PwDbOutputException(e) + } catch (e: InvalidKeyException) { + throw PwDbOutputException(e) + } + + dos = DigestOutputStream(os, md) + mos = MacOutputStream(dos, hmac) + los = LEDataOutputStream(mos) + } + + @Throws(IOException::class) + fun output() { + + los.writeUInt(PwDbHeader.PWM_DBSIG_1.toLong()) + los.writeUInt(PwDbHeaderV4.DBSIG_2.toLong()) + los.writeUInt(header.version) + + + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CipherID, Types.UUIDtoBytes(db.dataCipher)) + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.compressionAlgorithm.id)) + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.MasterSeed, header.masterSeed) + + if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformSeed, header.transformSeed) + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformRounds, LEDataOutputStream.writeLongBuf(db.numberKeyEncryptionRounds)) + } else { + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.kdfParameters)) + } + + if (header.encryptionIV.isNotEmpty()) { + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EncryptionIV, header.encryptionIV) + } + + if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey) + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes) + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomStreamID, LEDataOutputStream.writeIntBuf(header.innerRandomStream!!.id)) + } + + if (db.containsPublicCustomData()) { + val bos = ByteArrayOutputStream() + val los = LEDataOutputStream(bos) + VariantDictionary.serialize(db.publicCustomData, los) + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.PublicCustomData, bos.toByteArray()) + } + + writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EndOfHeader, EndHeaderValue) + + los.flush() + hashOfHeader = dos.messageDigest.digest() + headerHmac = mos.mac + } + + @Throws(IOException::class) + private fun writeHeaderField(fieldId: Byte, pbData: ByteArray?) { + // Write the field id + los.write(fieldId.toInt()) + + if (pbData != null) { + writeHeaderFieldSize(pbData.size) + los.write(pbData) + } else { + writeHeaderFieldSize(0) + } + } + + @Throws(IOException::class) + private fun writeHeaderFieldSize(size: Int) { + if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { + los.writeUShort(size) + } else { + los.writeInt(size) + } + + } + + companion object { + private val EndHeaderValue = byteArrayOf('\r'.toByte(), '\n'.toByte(), '\r'.toByte(), '\n'.toByte()) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt new file mode 100644 index 000000000..46703ffde --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbInnerHeaderOutputV4.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2017 Brian Pellin. + * + * This file is part of KeePass DX. + * + * KeePassDroid is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePassDroid is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePassDroid. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import com.kunzisoft.keepass.database.element.PwDatabaseV4 +import com.kunzisoft.keepass.database.file.PwDbHeaderV4 +import com.kunzisoft.keepass.database.element.security.ProtectedBinary +import com.kunzisoft.keepass.stream.LEDataOutputStream +import com.kunzisoft.keepass.utils.MemUtil + +import java.io.IOException +import java.io.OutputStream +import kotlin.experimental.or + +class PwDbInnerHeaderOutputV4(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os: OutputStream) { + + private val los: LEDataOutputStream = LEDataOutputStream(os) + + @Throws(IOException::class) + fun output() { + los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID.toInt()) + los.writeInt(4) + if (header.innerRandomStream == null) + throw IOException("Can't write innerRandomStream") + los.writeInt(header.innerRandomStream!!.id) + + val streamKeySize = header.innerRandomStreamKey.size + los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey.toInt()) + los.writeInt(streamKeySize) + los.write(header.innerRandomStreamKey) + + for (protectedBinary in db.binPool.binaries()) { + var flag = PwDbHeaderV4.KdbxBinaryFlags.None + if (protectedBinary.isProtected) { + flag = flag or PwDbHeaderV4.KdbxBinaryFlags.Protected + } + + los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary.toInt()) + los.writeInt(protectedBinary.length().toInt() + 1) // TODO verify + los.write(flag.toInt()) + + protectedBinary.getData()?.let { + MemUtil.readBytes(it) { buffer -> los.write(buffer) } + } ?: throw IOException("Can't write protected binary") + } + + los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.EndOfHeader.toInt()) + los.writeInt(0) + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt new file mode 100644 index 000000000..8cc712980 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import com.kunzisoft.keepass.database.file.PwDbHeader +import com.kunzisoft.keepass.database.exception.PwDbOutputException + +import java.io.OutputStream +import java.security.NoSuchAlgorithmException +import java.security.SecureRandom + +abstract class PwDbOutput
protected constructor(protected var mOS: OutputStream) { + + @Throws(PwDbOutputException::class) + protected open fun setIVs(header: Header): SecureRandom { + val random: SecureRandom + try { + random = SecureRandom.getInstance("SHA1PRNG") + } catch (e: NoSuchAlgorithmException) { + throw PwDbOutputException("Does not support secure random number generation.") + } + + random.nextBytes(header.encryptionIV) + random.nextBytes(header.masterSeed) + + return random + } + + @Throws(PwDbOutputException::class) + abstract fun output() + + @Throws(PwDbOutputException::class) + abstract fun outputHeader(outputStream: OutputStream): Header + +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt new file mode 100644 index 000000000..7b1b7dc12 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt @@ -0,0 +1,279 @@ +/* +` * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import com.kunzisoft.keepass.crypto.CipherFactory +import com.kunzisoft.keepass.database.element.* +import com.kunzisoft.keepass.database.exception.PwDbOutputException +import com.kunzisoft.keepass.database.file.PwDbHeader +import com.kunzisoft.keepass.database.file.PwDbHeaderV3 +import com.kunzisoft.keepass.stream.LEDataOutputStream +import com.kunzisoft.keepass.stream.NullOutputStream + +import javax.crypto.Cipher +import javax.crypto.CipherOutputStream +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec +import java.io.BufferedOutputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.OutputStream +import java.security.* +import java.util.ArrayList + +class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : PwDbOutput(os) { + + private var headerHashBlock: ByteArray? = null + + @Throws(PwDbOutputException::class) + fun getFinalKey(header: PwDbHeader): ByteArray { + try { + val h3 = header as PwDbHeaderV3 + mDatabaseV3.makeFinalKey(h3.masterSeed, h3.transformSeed, mDatabaseV3.numberKeyEncryptionRounds) + return mDatabaseV3.finalKey + } catch (e: IOException) { + throw PwDbOutputException("Key creation failed.", e) + } + + } + + @Throws(PwDbOutputException::class) + 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 + sortGroupsForOutput() + + val header = outputHeader(mOS) + + val finalKey = getFinalKey(header) + + val cipher: Cipher + cipher = try { + when { + mDatabaseV3.encryptionAlgorithm === PwEncryptionAlgorithm.AESRijndael-> + CipherFactory.getInstance("AES/CBC/PKCS5Padding") + mDatabaseV3.encryptionAlgorithm === PwEncryptionAlgorithm.Twofish -> + CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING") + else -> + throw Exception() + } + } catch (e: Exception) { + throw PwDbOutputException("Algorithm not supported.", e) + } + + try { + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(finalKey, "AES"), IvParameterSpec(header.encryptionIV)) + val cos = CipherOutputStream(mOS, cipher) + val bos = BufferedOutputStream(cos) + outputPlanGroupAndEntries(bos) + bos.flush() + bos.close() + + } catch (e: InvalidKeyException) { + throw PwDbOutputException("Invalid key", e) + } catch (e: InvalidAlgorithmParameterException) { + throw PwDbOutputException("Invalid algorithm parameter.", e) + } catch (e: IOException) { + throw PwDbOutputException("Failed to output final encrypted part.", e) + } + + } + + @Throws(PwDbOutputException::class) + override fun setIVs(header: PwDbHeaderV3): SecureRandom { + val random = super.setIVs(header) + random.nextBytes(header.transformSeed) + return random + } + + @Throws(PwDbOutputException::class) + override fun outputHeader(outputStream: OutputStream): PwDbHeaderV3 { + // Build header + val header = PwDbHeaderV3() + header.signature1 = PwDbHeader.PWM_DBSIG_1 + header.signature2 = PwDbHeaderV3.DBSIG_2 + header.flags = PwDbHeaderV3.FLAG_SHA2 + + if (mDatabaseV3.encryptionAlgorithm === PwEncryptionAlgorithm.AESRijndael) { + header.flags = header.flags or PwDbHeaderV3.FLAG_RIJNDAEL + } else if (mDatabaseV3.encryptionAlgorithm === PwEncryptionAlgorithm.Twofish) { + header.flags = header.flags or PwDbHeaderV3.FLAG_TWOFISH + } else { + throw PwDbOutputException("Unsupported algorithm.") + } + + header.version = PwDbHeaderV3.DBVER_DW + header.numGroups = mDatabaseV3.numberOfGroups() + header.numEntries = mDatabaseV3.numberOfEntries() + header.numKeyEncRounds = mDatabaseV3.numberKeyEncryptionRounds.toInt() + + setIVs(header) + + // Content checksum + var md: MessageDigest? = null + try { + md = MessageDigest.getInstance("SHA-256") + } catch (e: NoSuchAlgorithmException) { + throw PwDbOutputException("SHA-256 not implemented here.", e) + } + + // Header checksum + val headerDigest: MessageDigest + try { + headerDigest = MessageDigest.getInstance("SHA-256") + } catch (e: NoSuchAlgorithmException) { + throw PwDbOutputException("SHA-256 not implemented here.", e) + } + + var nos = NullOutputStream() + val headerDos = DigestOutputStream(nos, headerDigest) + + // Output header for the purpose of calculating the header checksum + var pho = PwDbHeaderOutputV3(header, headerDos) + try { + pho.outputStart() + pho.outputEnd() + headerDos.flush() + } catch (e: IOException) { + throw PwDbOutputException(e) + } + + val headerHash = headerDigest.digest() + headerHashBlock = getHeaderHashBuffer(headerHash) + + // Output database for the purpose of calculating the content checksum + nos = NullOutputStream() + val dos = DigestOutputStream(nos, md) + val bos = BufferedOutputStream(dos) + try { + outputPlanGroupAndEntries(bos) + bos.flush() + bos.close() + } catch (e: IOException) { + throw PwDbOutputException("Failed to generate checksum.", e) + } + + header.contentsHash = md!!.digest() + + // Output header for real output, containing content hash + pho = PwDbHeaderOutputV3(header, outputStream) + try { + pho.outputStart() + dos.on(false) + pho.outputContentHash() + dos.on(true) + pho.outputEnd() + dos.flush() + } catch (e: IOException) { + throw PwDbOutputException(e) + } + + return header + } + + @Throws(PwDbOutputException::class) + fun outputPlanGroupAndEntries(os: OutputStream) { + val los = LEDataOutputStream(os) + + // useHeaderHash + if (headerHashBlock != null) { + try { + los.writeUShort(0x0000) + los.writeInt(headerHashBlock!!.size) + los.write(headerHashBlock) + } catch (e: IOException) { + throw PwDbOutputException("Failed to output header hash.", e) + } + } + + // Groups + for (group in mDatabaseV3.groupIndexes) { + val pgo = PwGroupOutputV3(group, os) + try { + pgo.output() + } catch (e: IOException) { + throw PwDbOutputException("Failed to output a tree", e) + } + } + + // Entries + for (entry in mDatabaseV3.entryIndexes) { + val peo = PwEntryOutputV3(entry, os) + try { + peo.output() + } catch (e: IOException) { + throw PwDbOutputException("Failed to output an entry.", e) + } + } + } + + private fun sortGroupsForOutput() { + val groupList = ArrayList() + // Rebuild list according to coalation sorting order removing any orphaned groups + for (rootGroup in mDatabaseV3.rootGroups) { + sortGroup(rootGroup, groupList) + } + mDatabaseV3.setGroupIndexes(groupList) + } + + private fun sortGroup(group: PwGroupV3, groupList: MutableList) { + // Add current tree + groupList.add(group) + + // Recurse over children + for (childGroup in group.getChildGroups()) { + sortGroup(childGroup, groupList) + } + } + + private fun getHeaderHashBuffer(headerDigest: ByteArray): ByteArray? { + return try { + val byteArrayOutputStream = ByteArrayOutputStream() + writeExtData(headerDigest, byteArrayOutputStream) + byteArrayOutputStream.toByteArray() + } catch (e: IOException) { + null + } + + } + + @Throws(IOException::class) + private fun writeExtData(headerDigest: ByteArray, os: OutputStream) { + val los = LEDataOutputStream(os) + + writeExtDataField(los, 0x0001, headerDigest, headerDigest.size) + val headerRandom = ByteArray(32) + val rand = SecureRandom() + rand.nextBytes(headerRandom) + writeExtDataField(los, 0x0002, headerRandom, headerRandom.size) + writeExtDataField(los, 0xFFFF, null, 0) + + } + + @Throws(IOException::class) + private fun writeExtDataField(los: LEDataOutputStream, fieldType: Int, data: ByteArray?, fieldSize: Int) { + los.writeUShort(fieldType) + los.writeInt(fieldSize) + if (data != null) { + los.write(data) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt new file mode 100644 index 000000000..9dfc7547b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt @@ -0,0 +1,743 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import android.util.Log +import android.util.Xml +import biz.source_code.base64Coder.Base64Coder +import com.kunzisoft.keepass.crypto.CipherFactory +import com.kunzisoft.keepass.crypto.CrsAlgorithm +import com.kunzisoft.keepass.crypto.PwStreamCipherFactory +import com.kunzisoft.keepass.crypto.engine.CipherEngine +import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory +import com.kunzisoft.keepass.database.* +import com.kunzisoft.keepass.database.element.* +import com.kunzisoft.keepass.database.exception.PwDbOutputException +import com.kunzisoft.keepass.database.exception.UnknownKDF +import com.kunzisoft.keepass.database.file.PwCompressionAlgorithm +import com.kunzisoft.keepass.database.file.PwDbHeaderV4 +import com.kunzisoft.keepass.database.element.security.ProtectedBinary +import com.kunzisoft.keepass.database.element.security.ProtectedString +import com.kunzisoft.keepass.stream.HashedBlockOutputStream +import com.kunzisoft.keepass.stream.HmacBlockOutputStream +import com.kunzisoft.keepass.stream.LEDataOutputStream +import com.kunzisoft.keepass.utils.DateUtil +import com.kunzisoft.keepass.utils.EmptyUtils +import com.kunzisoft.keepass.utils.MemUtil +import com.kunzisoft.keepass.utils.Types +import org.joda.time.DateTime +import org.spongycastle.crypto.StreamCipher +import org.xmlpull.v1.XmlSerializer + +import javax.crypto.Cipher +import javax.crypto.CipherOutputStream +import java.io.IOException +import java.io.OutputStream +import java.security.NoSuchAlgorithmException +import java.security.SecureRandom +import java.util.* +import java.util.zip.GZIPOutputStream + +class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputStream) : PwDbOutput(outputStream) { + + private var randomStream: StreamCipher? = null + private lateinit var xml: XmlSerializer + private var header: PwDbHeaderV4? = null + private var hashOfHeader: ByteArray? = null + private var headerHmac: ByteArray? = null + private var engine: CipherEngine? = null + + @Throws(PwDbOutputException::class) + override fun output() { + + try { + try { + engine = CipherFactory.getInstance(mDatabaseV4.dataCipher) + } catch (e: NoSuchAlgorithmException) { + throw PwDbOutputException("No such cipher", e) + } + + header = outputHeader(mOS) + + val osPlain: OutputStream + if (header!!.version < PwDbHeaderV4.FILE_VERSION_32_4) { + val cos = attachStreamEncryptor(header!!, mOS) + cos.write(header!!.streamStartBytes) + + osPlain = HashedBlockOutputStream(cos) + } else { + mOS.write(hashOfHeader!!) + mOS.write(headerHmac!!) + + val hbos = HmacBlockOutputStream(mOS, mDatabaseV4.hmacKey) + osPlain = attachStreamEncryptor(header!!, hbos) + } + + val osXml: OutputStream + try { + if (mDatabaseV4.compressionAlgorithm === PwCompressionAlgorithm.Gzip) { + osXml = GZIPOutputStream(osPlain) + } else { + osXml = osPlain + } + + if (header!!.version >= PwDbHeaderV4.FILE_VERSION_32_4) { + val ihOut = PwDbInnerHeaderOutputV4(mDatabaseV4, header!!, osXml) + ihOut.output() + } + + outputDatabase(osXml) + osXml.close() + } catch (e: IllegalArgumentException) { + throw PwDbOutputException(e) + } catch (e: IllegalStateException) { + throw PwDbOutputException(e) + } + + } catch (e: IOException) { + throw PwDbOutputException(e) + } + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun outputDatabase(os: OutputStream) { + + xml = Xml.newSerializer() + + xml.setOutput(os, "UTF-8") + xml.startDocument("UTF-8", true) + + xml.startTag(null, PwDatabaseV4XML.ElemDocNode) + + writeMeta() + + val root = mDatabaseV4.rootGroup + xml.startTag(null, PwDatabaseV4XML.ElemRoot) + startGroup(root) + val groupStack = Stack() + groupStack.push(root) + + if (!root.doForEachChild( + object : NodeHandler() { + override fun operate(node: PwEntryV4): Boolean { + try { + writeEntry(node, false) + } catch (ex: IOException) { + throw RuntimeException(ex) + } + + return true + } + }, + object : NodeHandler() { + override fun operate(node: PwGroupV4): Boolean { + while (true) { + try { + if (node.parent === groupStack.peek()) { + groupStack.push(node) + startGroup(node) + break + } else { + groupStack.pop() + if (groupStack.size <= 0) return false + endGroup() + } + } catch (e: IOException) { + throw RuntimeException(e) + } + + } + return true + } + })) + throw RuntimeException("Writing groups failed") + + while (groupStack.size > 1) { + xml.endTag(null, PwDatabaseV4XML.ElemGroup) + groupStack.pop() + } + + endGroup() + + writeList(PwDatabaseV4XML.ElemDeletedObjects, mDatabaseV4.deletedObjects) + + xml.endTag(null, PwDatabaseV4XML.ElemRoot) + + xml.endTag(null, PwDatabaseV4XML.ElemDocNode) + xml.endDocument() + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeMeta() { + xml.startTag(null, PwDatabaseV4XML.ElemMeta) + + writeObject(PwDatabaseV4XML.ElemGenerator, mDatabaseV4.localizedAppName) + + if (hashOfHeader != null) { + writeObject(PwDatabaseV4XML.ElemHeaderHash, String(Base64Coder.encode(hashOfHeader!!))) + } + + writeObject(PwDatabaseV4XML.ElemDbName, mDatabaseV4.name, true) + writeObject(PwDatabaseV4XML.ElemDbNameChanged, mDatabaseV4.nameChanged.date) + writeObject(PwDatabaseV4XML.ElemDbDesc, mDatabaseV4.description, true) + writeObject(PwDatabaseV4XML.ElemDbDescChanged, mDatabaseV4.descriptionChanged.date) + writeObject(PwDatabaseV4XML.ElemDbDefaultUser, mDatabaseV4.defaultUserName, true) + writeObject(PwDatabaseV4XML.ElemDbDefaultUserChanged, mDatabaseV4.defaultUserNameChanged.date) + writeObject(PwDatabaseV4XML.ElemDbMntncHistoryDays, mDatabaseV4.maintenanceHistoryDays) + writeObject(PwDatabaseV4XML.ElemDbColor, mDatabaseV4.color) + writeObject(PwDatabaseV4XML.ElemDbKeyChanged, mDatabaseV4.keyLastChanged.date) + writeObject(PwDatabaseV4XML.ElemDbKeyChangeRec, mDatabaseV4.keyChangeRecDays) + writeObject(PwDatabaseV4XML.ElemDbKeyChangeForce, mDatabaseV4.keyChangeForceDays) + + writeList(PwDatabaseV4XML.ElemMemoryProt, mDatabaseV4.memoryProtection) + + writeCustomIconList() + + writeObject(PwDatabaseV4XML.ElemRecycleBinEnabled, mDatabaseV4.isRecycleBinEnabled) + writeObject(PwDatabaseV4XML.ElemRecycleBinUuid, mDatabaseV4.recycleBinUUID) + writeObject(PwDatabaseV4XML.ElemRecycleBinChanged, mDatabaseV4.recycleBinChanged) + writeObject(PwDatabaseV4XML.ElemEntryTemplatesGroup, mDatabaseV4.entryTemplatesGroup) + writeObject(PwDatabaseV4XML.ElemEntryTemplatesGroupChanged, mDatabaseV4.entryTemplatesGroupChanged.date) + writeObject(PwDatabaseV4XML.ElemHistoryMaxItems, mDatabaseV4.historyMaxItems.toLong()) + writeObject(PwDatabaseV4XML.ElemHistoryMaxSize, mDatabaseV4.historyMaxSize) + writeObject(PwDatabaseV4XML.ElemLastSelectedGroup, mDatabaseV4.lastSelectedGroup) + writeObject(PwDatabaseV4XML.ElemLastTopVisibleGroup, mDatabaseV4.lastTopVisibleGroup) + + if (header!!.version < PwDbHeaderV4.FILE_VERSION_32_4) { + writeBinPool() + } + writeList(PwDatabaseV4XML.ElemCustomData, mDatabaseV4.customData) + + xml.endTag(null, PwDatabaseV4XML.ElemMeta) + } + + @Throws(PwDbOutputException::class) + private fun attachStreamEncryptor(header: PwDbHeaderV4, os: OutputStream): CipherOutputStream { + val cipher: Cipher + try { + //mDatabaseV4.makeFinalKey(header.masterSeed, mDatabaseV4.kdfParameters); + + cipher = engine!!.getCipher(Cipher.ENCRYPT_MODE, mDatabaseV4.finalKey, header.encryptionIV) + } catch (e: Exception) { + throw PwDbOutputException("Invalid algorithm.", e) + } + + return CipherOutputStream(os, cipher) + } + + @Throws(PwDbOutputException::class) + override fun setIVs(header: PwDbHeaderV4): SecureRandom { + val random = super.setIVs(header) + random.nextBytes(header.masterSeed) + + val ivLength = engine!!.ivLength() + if (ivLength != header.encryptionIV.size) { + header.encryptionIV = ByteArray(ivLength) + } + random.nextBytes(header.encryptionIV) + + if (mDatabaseV4.kdfParameters == null) { + mDatabaseV4.kdfParameters = KdfFactory.aesKdf.defaultParameters + } + + try { + val kdf = KdfFactory.getEngineV4(mDatabaseV4.kdfParameters) + kdf.randomize(mDatabaseV4.kdfParameters) + } catch (unknownKDF: UnknownKDF) { + Log.e(TAG, "Unable to retrieve header", unknownKDF) + } + + if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { + header.innerRandomStream = CrsAlgorithm.Salsa20 + header.innerRandomStreamKey = ByteArray(32) + } else { + header.innerRandomStream = CrsAlgorithm.ChaCha20 + header.innerRandomStreamKey = ByteArray(64) + } + random.nextBytes(header.innerRandomStreamKey) + + randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey) + if (randomStream == null) { + throw PwDbOutputException("Invalid random cipher") + } + + if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) { + random.nextBytes(header.streamStartBytes) + } + + return random + } + + @Throws(PwDbOutputException::class) + override fun outputHeader(outputStream: OutputStream): PwDbHeaderV4 { + + val header = PwDbHeaderV4(mDatabaseV4) + setIVs(header) + + val pho = PwDbHeaderOutputV4(mDatabaseV4, header, outputStream) + try { + pho.output() + } catch (e: IOException) { + throw PwDbOutputException("Failed to output the header.", e) + } + + hashOfHeader = pho.hashOfHeader + headerHmac = pho.headerHmac + + return header + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun startGroup(group: PwGroupV4) { + xml.startTag(null, PwDatabaseV4XML.ElemGroup) + writeObject(PwDatabaseV4XML.ElemUuid, group.id) + writeObject(PwDatabaseV4XML.ElemName, group.title) + writeObject(PwDatabaseV4XML.ElemNotes, group.notes) + writeObject(PwDatabaseV4XML.ElemIcon, group.icon.iconId.toLong()) + + if (group.iconCustom != PwIconCustom.ZERO) { + writeObject(PwDatabaseV4XML.ElemCustomIconID, group.iconCustom.uuid) + } + + writeList(PwDatabaseV4XML.ElemTimes, group) + writeObject(PwDatabaseV4XML.ElemIsExpanded, group.isExpanded) + writeObject(PwDatabaseV4XML.ElemGroupDefaultAutoTypeSeq, group.defaultAutoTypeSequence) + writeObject(PwDatabaseV4XML.ElemEnableAutoType, group.enableAutoType) + writeObject(PwDatabaseV4XML.ElemEnableSearching, group.enableSearching) + writeObject(PwDatabaseV4XML.ElemLastTopVisibleEntry, group.lastTopVisibleEntry) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun endGroup() { + xml.endTag(null, PwDatabaseV4XML.ElemGroup) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeEntry(entry: PwEntryV4, isHistory: Boolean) { + + xml.startTag(null, PwDatabaseV4XML.ElemEntry) + + writeObject(PwDatabaseV4XML.ElemUuid, entry.id) + writeObject(PwDatabaseV4XML.ElemIcon, entry.icon.iconId.toLong()) + + if (entry.iconCustom != PwIconCustom.ZERO) { + writeObject(PwDatabaseV4XML.ElemCustomIconID, entry.iconCustom.uuid) + } + + writeObject(PwDatabaseV4XML.ElemFgColor, entry.foregroundColor) + writeObject(PwDatabaseV4XML.ElemBgColor, entry.backgroundColor) + writeObject(PwDatabaseV4XML.ElemOverrideUrl, entry.overrideURL) + writeObject(PwDatabaseV4XML.ElemTags, entry.tags) + + writeList(PwDatabaseV4XML.ElemTimes, entry) + + writeList(entry.fields.listOfAllFields, true) + writeList(entry.binaries) + writeList(PwDatabaseV4XML.ElemAutoType, entry.autoType) + + if (!isHistory) { + writeList(PwDatabaseV4XML.ElemHistory, entry.history, true) + } + // else entry.sizeOfHistory() == 0 + + xml.endTag(null, PwDatabaseV4XML.ElemEntry) + } + + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(key: String, value: ProtectedBinary) { + + xml.startTag(null, PwDatabaseV4XML.ElemBinary) + xml.startTag(null, PwDatabaseV4XML.ElemKey) + xml.text(safeXmlString(key)) + xml.endTag(null, PwDatabaseV4XML.ElemKey) + + xml.startTag(null, PwDatabaseV4XML.ElemValue) + val ref = mDatabaseV4.binPool.findKey(value) + val strRef = Integer.toString(ref) + + if (strRef != null) { + xml.attribute(null, PwDatabaseV4XML.AttrRef, strRef) + } else { + subWriteValue(value) + } + xml.endTag(null, PwDatabaseV4XML.ElemValue) + + xml.endTag(null, PwDatabaseV4XML.ElemBinary) + } + + /* + TODO Make with pipe + private void subWriteValue(ProtectedBinary value) throws IllegalArgumentException, IllegalStateException, IOException { + try (InputStream inputStream = value.getData()) { + if (inputStream == null) { + Log.e(TAG, "Can't write a null input stream."); + return; + } + + if (value.isProtected()) { + xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue); + + try (InputStream cypherInputStream = + IOUtil.pipe(inputStream, + o -> new org.spongycastle.crypto.io.CipherOutputStream(o, randomStream))) { + writeInputStreamInBase64(cypherInputStream); + } + + } else { + if (mDatabaseV4.getCompressionAlgorithm() == PwCompressionAlgorithm.Gzip) { + + xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue); + + try (InputStream gZipInputStream = + IOUtil.pipe(inputStream, GZIPOutputStream::new, (int) value.length())) { + writeInputStreamInBase64(gZipInputStream); + } + + } else { + writeInputStreamInBase64(inputStream); + } + } + } + } + + private void writeInputStreamInBase64(InputStream inputStream) throws IOException { + try (InputStream base64InputStream = + IOUtil.pipe(inputStream, + o -> new Base64OutputStream(o, DEFAULT))) { + MemUtil.readBytes(base64InputStream, + buffer -> xml.text(Arrays.toString(buffer))); + } + } + */ + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun subWriteValue(value: ProtectedBinary) { + + val valLength = value.length().toInt() + if (valLength > 0) { + val buffer = ByteArray(valLength) + if (valLength == value.getData()!!.read(buffer, 0, valLength)) { + + if (value.isProtected) { + xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue) + + val encoded = ByteArray(valLength) + randomStream!!.processBytes(buffer, 0, valLength, encoded, 0) + xml.text(String(Base64Coder.encode(encoded))) + + } else { + if (mDatabaseV4.compressionAlgorithm === PwCompressionAlgorithm.Gzip) { + xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue) + + val compressData = MemUtil.compress(buffer) + xml.text(String(Base64Coder.encode(compressData))) + + } else { + xml.text(String(Base64Coder.encode(buffer))) + } + } + } else { + Log.e(TAG, "Unable to read the stream of the protected binary") + } + } + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(name: String, value: String, filterXmlChars: Boolean = false) { + var xmlString = value + + xml.startTag(null, name) + + if (filterXmlChars) { + xmlString = safeXmlString(xmlString) + } + + xml.text(xmlString) + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(name: String, value: Date?) { + if (header!!.version < PwDbHeaderV4.FILE_VERSION_32_4) { + writeObject(name, PwDatabaseV4XML.dateFormatter.get().format(value)) + } else { + val dt = DateTime(value) + val seconds = DateUtil.convertDateToKDBX4Time(dt) + val buf = LEDataOutputStream.writeLongBuf(seconds) + val b64 = String(Base64Coder.encode(buf)) + writeObject(name, b64) + } + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(name: String, value: Long) { + writeObject(name, value.toString()) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(name: String, value: Boolean?) { + val text: String = when { + value == null -> "null" + value -> PwDatabaseV4XML.ValTrue + else -> PwDatabaseV4XML.ValFalse + } + + writeObject(name, text) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(name: String, uuid: UUID) { + val data = Types.UUIDtoBytes(uuid) + writeObject(name, String(Base64Coder.encode(data))) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(name: String, keyName: String, keyValue: String, valueName: String, valueValue: String) { + xml.startTag(null, name) + + xml.startTag(null, keyName) + xml.text(safeXmlString(keyValue)) + xml.endTag(null, keyName) + + xml.startTag(null, valueName) + xml.text(safeXmlString(valueValue)) + xml.endTag(null, valueName) + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(name: String, autoType: AutoType) { + xml.startTag(null, name) + + writeObject(PwDatabaseV4XML.ElemAutoTypeEnabled, autoType.enabled) + writeObject(PwDatabaseV4XML.ElemAutoTypeObfuscation, autoType.obfuscationOptions) + + if (autoType.defaultSequence.isNotEmpty()) { + writeObject(PwDatabaseV4XML.ElemAutoTypeDefaultSeq, autoType.defaultSequence, true) + } + + for ((key, value) in autoType.entrySet()) { + writeObject(PwDatabaseV4XML.ElemAutoTypeItem, PwDatabaseV4XML.ElemWindow, key, PwDatabaseV4XML.ElemKeystrokeSequence, value) + } + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(strings: Map, isEntryString: Boolean) { + + for ((key, value) in strings) { + writeObject(key, value, isEntryString) + } + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(key: String, value: ProtectedString, isEntryString: Boolean) { + + xml.startTag(null, PwDatabaseV4XML.ElemString) + xml.startTag(null, PwDatabaseV4XML.ElemKey) + xml.text(safeXmlString(key)) + xml.endTag(null, PwDatabaseV4XML.ElemKey) + + xml.startTag(null, PwDatabaseV4XML.ElemValue) + var protect = value.isProtected + if (isEntryString) { + when (key) { + MemoryProtectionConfig.ProtectDefinition.TITLE_FIELD -> protect = mDatabaseV4.memoryProtection.protectTitle + MemoryProtectionConfig.ProtectDefinition.USERNAME_FIELD -> protect = mDatabaseV4.memoryProtection.protectUserName + MemoryProtectionConfig.ProtectDefinition.PASSWORD_FIELD -> protect = mDatabaseV4.memoryProtection.protectPassword + MemoryProtectionConfig.ProtectDefinition.URL_FIELD -> protect = mDatabaseV4.memoryProtection.protectUrl + MemoryProtectionConfig.ProtectDefinition.NOTES_FIELD -> protect = mDatabaseV4.memoryProtection.protectNotes + } + } + + if (protect) { + xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue) + + val data = value.toString().toByteArray(charset("UTF-8")) + val valLength = data.size + + if (valLength > 0) { + val encoded = ByteArray(valLength) + randomStream!!.processBytes(data, 0, valLength, encoded, 0) + xml.text(String(Base64Coder.encode(encoded))) + } + } else { + xml.text(safeXmlString(value.toString())) + } + + xml.endTag(null, PwDatabaseV4XML.ElemValue) + xml.endTag(null, PwDatabaseV4XML.ElemString) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeObject(name: String?, value: PwDeletedObject) { + assert(name != null) + + xml.startTag(null, name) + + writeObject(PwDatabaseV4XML.ElemUuid, value.uuid) + writeObject(PwDatabaseV4XML.ElemDeletionTime, value.deletionTime) + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(binaries: Map) { + for ((key, value) in binaries) { + writeObject(key, value) + } + } + + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(name: String?, value: List) { + assert(name != null) + + xml.startTag(null, name) + + for (pdo in value) { + writeObject(PwDatabaseV4XML.ElemDeletedObject, pdo) + } + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(name: String?, value: MemoryProtectionConfig) { + assert(name != null) + + xml.startTag(null, name) + + writeObject(PwDatabaseV4XML.ElemProtTitle, value.protectTitle) + writeObject(PwDatabaseV4XML.ElemProtUserName, value.protectUserName) + writeObject(PwDatabaseV4XML.ElemProtPassword, value.protectPassword) + writeObject(PwDatabaseV4XML.ElemProtURL, value.protectUrl) + writeObject(PwDatabaseV4XML.ElemProtNotes, value.protectNotes) + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(name: String?, customData: Map) { + assert(name != null) + + xml.startTag(null, name) + + for ((key, value) in customData) { + writeObject(PwDatabaseV4XML.ElemStringDictExItem, PwDatabaseV4XML.ElemKey, key, PwDatabaseV4XML.ElemValue, value) + + } + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(name: String?, it: NodeV4Interface) { + assert(name != null) + + xml.startTag(null, name) + + writeObject(PwDatabaseV4XML.ElemLastModTime, it.lastModificationTime.date) + writeObject(PwDatabaseV4XML.ElemCreationTime, it.creationTime.date) + writeObject(PwDatabaseV4XML.ElemLastAccessTime, it.lastAccessTime.date) + writeObject(PwDatabaseV4XML.ElemExpiryTime, it.expiryTime.date) + writeObject(PwDatabaseV4XML.ElemExpires, it.isExpires) + writeObject(PwDatabaseV4XML.ElemUsageCount, it.usageCount) + writeObject(PwDatabaseV4XML.ElemLocationChanged, it.locationChanged.date) + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeList(name: String?, value: List, isHistory: Boolean) { + assert(name != null) + + xml.startTag(null, name) + + for (entry in value) { + writeEntry(entry, isHistory) + } + + xml.endTag(null, name) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeCustomIconList() { + val customIcons = mDatabaseV4.customIcons + if (customIcons.size == 0) return + + xml.startTag(null, PwDatabaseV4XML.ElemCustomIcons) + + for (icon in customIcons) { + xml.startTag(null, PwDatabaseV4XML.ElemCustomIconItem) + + writeObject(PwDatabaseV4XML.ElemCustomIconItemID, icon.uuid) + writeObject(PwDatabaseV4XML.ElemCustomIconItemData, String(Base64Coder.encode(icon.imageData))) + + xml.endTag(null, PwDatabaseV4XML.ElemCustomIconItem) + } + + xml.endTag(null, PwDatabaseV4XML.ElemCustomIcons) + } + + @Throws(IllegalArgumentException::class, IllegalStateException::class, IOException::class) + private fun writeBinPool() { + xml.startTag(null, PwDatabaseV4XML.ElemBinaries) + + for ((key, value) in mDatabaseV4.binPool.entrySet()) { + xml.startTag(null, PwDatabaseV4XML.ElemBinary) + xml.attribute(null, PwDatabaseV4XML.AttrId, Integer.toString(key)) + + subWriteValue(value) + + xml.endTag(null, PwDatabaseV4XML.ElemBinary) + + } + + xml.endTag(null, PwDatabaseV4XML.ElemBinaries) + } + + private fun safeXmlString(text: String): String { + if (EmptyUtils.isNullOrEmpty(text)) { + return text + } + + val stringBuilder = StringBuilder() + var ch: Char + for (i in 0 until text.length) { + ch = text[i] + if ( + ch.toInt() in 0x20..0xD7FF || + ch.toInt() == 0x9 || ch.toInt() == 0xA || ch.toInt() == 0xD || + ch.toInt() in 0xE000..0xFFFD + ) { + stringBuilder.append(ch) + } + } + return stringBuilder.toString() + } + + companion object { + private val TAG = PwDbV4Output::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt new file mode 100644 index 000000000..a24c7c1e6 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt @@ -0,0 +1,166 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import com.kunzisoft.keepass.database.element.PwEntryV3 +import com.kunzisoft.keepass.stream.LEDataOutputStream +import com.kunzisoft.keepass.utils.Types + +import java.io.IOException +import java.io.OutputStream + +class PwEntryOutputV3 +/** + * Output the PwGroupV3 to the stream + */ +(private val mPE: PwEntryV3, private val mOS: OutputStream) { + /** + * Returns the number of bytes written by the stream + * @return Number of bytes written + */ + var length: Long = 0 + private set + + //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int + @Throws(IOException::class) + fun output() { + + length += 134 // Length of fixed size fields + + // UUID + mOS.write(UUID_FIELD_TYPE) + mOS.write(UUID_FIELD_SIZE) + mOS.write(Types.UUIDtoBytes(mPE.id)) + + // Group ID + mOS.write(GROUPID_FIELD_TYPE) + mOS.write(LONG_FOUR) + mOS.write(LEDataOutputStream.writeIntBuf(mPE.parent!!.id)) + + // Image ID + mOS.write(IMAGEID_FIELD_TYPE) + mOS.write(LONG_FOUR) + mOS.write(LEDataOutputStream.writeIntBuf(mPE.icon.iconId)) + + // Title + //byte[] title = mPE.title.getBytes("UTF-8"); + mOS.write(TITLE_FIELD_TYPE) + val titleLen = Types.writeCString(mPE.title, mOS) + length += titleLen.toLong() + + // URL + mOS.write(URL_FIELD_TYPE) + val urlLen = Types.writeCString(mPE.url, mOS) + length += urlLen.toLong() + + // Username + mOS.write(USERNAME_FIELD_TYPE) + val userLen = Types.writeCString(mPE.username, mOS) + length += userLen.toLong() + + // Password + val password = mPE.passwordBytes + mOS.write(PASSWORD_FIELD_TYPE) + mOS.write(LEDataOutputStream.writeIntBuf(password.size + 1)) + mOS.write(password) + mOS.write(0) + length += (password.size + 1).toLong() + + // Additional + mOS.write(ADDITIONAL_FIELD_TYPE) + val addlLen = Types.writeCString(mPE.notes, mOS) + length += addlLen.toLong() + + // Create date + writeDate(CREATE_FIELD_TYPE, mPE.creationTime.cDate) + + // Modification date + writeDate(MOD_FIELD_TYPE, mPE.lastModificationTime.cDate) + + // Access date + writeDate(ACCESS_FIELD_TYPE, mPE.lastAccessTime.cDate) + + // Expiration date + writeDate(EXPIRE_FIELD_TYPE, mPE.expiryTime.cDate) + + // Binary desc + mOS.write(BINARY_DESC_FIELD_TYPE) + val descLen = Types.writeCString(mPE.binaryDesc, mOS) + length += descLen.toLong() + + // Binary data + val dataLen = writeByteArray(mPE.binaryData) + length += dataLen.toLong() + + // End + mOS.write(END_FIELD_TYPE) + mOS.write(ZERO_FIELD_SIZE) + } + + @Throws(IOException::class) + private fun writeByteArray(data: ByteArray?): Int { + val dataLen: Int = data?.size ?: 0 + mOS.write(BINARY_DATA_FIELD_TYPE) + mOS.write(LEDataOutputStream.writeIntBuf(dataLen)) + if (data != null) { + mOS.write(data) + } + + return dataLen + } + + @Throws(IOException::class) + private fun writeDate(type: ByteArray, date: ByteArray?) { + mOS.write(type) + mOS.write(DATE_FIELD_SIZE) + if (date != null) { + mOS.write(date) + } else { + mOS.write(ZERO_FIVE) + } + } + + companion object { + // Constants + val UUID_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(1) + val GROUPID_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(2) + val IMAGEID_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(3) + val TITLE_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(4) + val URL_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(5) + val USERNAME_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(6) + val PASSWORD_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(7) + val ADDITIONAL_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(8) + val CREATE_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(9) + val MOD_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(10) + val ACCESS_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(11) + val EXPIRE_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(12) + val BINARY_DESC_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(13) + val BINARY_DATA_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(14) + val END_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(0xFFFF) + val LONG_FOUR:ByteArray = LEDataOutputStream.writeIntBuf(4) + val UUID_FIELD_SIZE:ByteArray = LEDataOutputStream.writeIntBuf(16) + val DATE_FIELD_SIZE:ByteArray = LEDataOutputStream.writeIntBuf(5) + val IMAGEID_FIELD_SIZE:ByteArray = LONG_FOUR + val LEVEL_FIELD_SIZE:ByteArray = LONG_FOUR + val FLAGS_FIELD_SIZE:ByteArray = LONG_FOUR + val ZERO_FIELD_SIZE:ByteArray = LEDataOutputStream.writeIntBuf(0) + val ZERO_FIVE:ByteArray = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt new file mode 100644 index 000000000..247b9c5ae --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.file.save + +import com.kunzisoft.keepass.database.element.PwGroupV3 +import com.kunzisoft.keepass.stream.LEDataOutputStream +import com.kunzisoft.keepass.utils.Types + +import java.io.IOException +import java.io.OutputStream + +class PwGroupOutputV3 +/** Output the PwGroupV3 to the stream + * @param pg + * @param os + */ +(private val mPG: PwGroupV3, private val mOS: OutputStream) { + + @Throws(IOException::class) + fun output() { + //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int, but most values can't be greater than 2^31, so it probably doesn't matter. + + // Group ID + mOS.write(GROUPID_FIELD_TYPE) + mOS.write(GROUPID_FIELD_SIZE) + mOS.write(LEDataOutputStream.writeIntBuf(mPG.id)) + + // Name + mOS.write(NAME_FIELD_TYPE) + Types.writeCString(mPG.title, mOS) + + // Create date + mOS.write(CREATE_FIELD_TYPE) + mOS.write(DATE_FIELD_SIZE) + mOS.write(mPG.creationTime.cDate) + + // Modification date + mOS.write(MOD_FIELD_TYPE) + mOS.write(DATE_FIELD_SIZE) + mOS.write(mPG.lastModificationTime.cDate) + + // Access date + mOS.write(ACCESS_FIELD_TYPE) + mOS.write(DATE_FIELD_SIZE) + mOS.write(mPG.lastAccessTime.cDate) + + // Expiration date + mOS.write(EXPIRE_FIELD_TYPE) + mOS.write(DATE_FIELD_SIZE) + mOS.write(mPG.expiryTime.cDate) + + // Image ID + mOS.write(IMAGEID_FIELD_TYPE) + mOS.write(IMAGEID_FIELD_SIZE) + mOS.write(LEDataOutputStream.writeIntBuf(mPG.icon.iconId)) + + // Level + mOS.write(LEVEL_FIELD_TYPE) + mOS.write(LEVEL_FIELD_SIZE) + mOS.write(LEDataOutputStream.writeUShortBuf(mPG.level)) + + // Flags + mOS.write(FLAGS_FIELD_TYPE) + mOS.write(FLAGS_FIELD_SIZE) + mOS.write(LEDataOutputStream.writeIntBuf(mPG.flags)) + + // End + mOS.write(END_FIELD_TYPE) + mOS.write(ZERO_FIELD_SIZE) + } + + companion object { + // Constants + val GROUPID_FIELD_TYPE: ByteArray = LEDataOutputStream.writeUShortBuf(1) + val NAME_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(2) + val CREATE_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(3) + val MOD_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(4) + val ACCESS_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(5) + val EXPIRE_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(6) + val IMAGEID_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(7) + val LEVEL_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(8) + val FLAGS_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(9) + val END_FIELD_TYPE:ByteArray = LEDataOutputStream.writeUShortBuf(0xFFFF) + val LONG_FOUR:ByteArray = LEDataOutputStream.writeIntBuf(4) + val GROUPID_FIELD_SIZE:ByteArray = LONG_FOUR + val DATE_FIELD_SIZE:ByteArray = LEDataOutputStream.writeIntBuf(5) + val IMAGEID_FIELD_SIZE:ByteArray = LONG_FOUR + val LEVEL_FIELD_SIZE:ByteArray = LEDataOutputStream.writeIntBuf(2) + val FLAGS_FIELD_SIZE:ByteArray = LONG_FOUR + val ZERO_FIELD_SIZE:ByteArray = LEDataOutputStream.writeIntBuf(0) + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java deleted file mode 100644 index ad40de81d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV3.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.database.element.PwDbHeaderV3; -import com.kunzisoft.keepass.stream.LEDataOutputStream; - -import java.io.IOException; -import java.io.OutputStream; - -public class PwDbHeaderOutputV3 { - private PwDbHeaderV3 mHeader; - private OutputStream mOS; - - public PwDbHeaderOutputV3(PwDbHeaderV3 header, OutputStream os) { - mHeader = header; - mOS = os; - } - - public void outputStart() throws IOException { - mOS.write(LEDataOutputStream.writeIntBuf(mHeader.signature1)); - mOS.write(LEDataOutputStream.writeIntBuf(mHeader.signature2)); - mOS.write(LEDataOutputStream.writeIntBuf(mHeader.flags)); - mOS.write(LEDataOutputStream.writeIntBuf(mHeader.version)); - mOS.write(mHeader.getMasterSeed()); - mOS.write(mHeader.getEncryptionIV()); - mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numGroups)); - mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numEntries)); - } - - public void outputContentHash() throws IOException { - mOS.write(mHeader.contentsHash); - } - - public void outputEnd() throws IOException { - mOS.write(mHeader.transformSeed); - mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numKeyEncRounds)); - } - - public void output() throws IOException { - outputStart(); - outputContentHash(); - outputEnd(); - } - - public void close() throws IOException { - mOS.close(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java deleted file mode 100644 index eee7b7b2a..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbHeaderOutputV4.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.collections.VariantDictionary; -import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters; -import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwDbHeader; -import com.kunzisoft.keepass.database.element.PwDbHeaderV4; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; -import com.kunzisoft.keepass.stream.HmacBlockStream; -import com.kunzisoft.keepass.stream.LEDataOutputStream; -import com.kunzisoft.keepass.stream.MacOutputStream; -import com.kunzisoft.keepass.utils.Types; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.DigestOutputStream; -import java.security.InvalidKeyException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - -public class PwDbHeaderOutputV4 extends PwDbHeaderOutput { - private PwDbHeaderV4 header; - private LEDataOutputStream los; - private MacOutputStream mos; - private DigestOutputStream dos; - private PwDatabaseV4 db; - public byte[] headerHmac; - - private static byte[] EndHeaderValue = {'\r', '\n', '\r', '\n'}; - - public PwDbHeaderOutputV4(PwDatabaseV4 d, PwDbHeaderV4 h, OutputStream os) throws PwDbOutputException { - db = d; - header = h; - - MessageDigest md; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new PwDbOutputException("SHA-256 not implemented here."); - } - - try { - d.makeFinalKey(header.getMasterSeed()); - } catch (IOException e) { - throw new PwDbOutputException(e); - } - - Mac hmac; - try { - hmac = Mac.getInstance("HmacSHA256"); - SecretKeySpec signingKey = new SecretKeySpec(HmacBlockStream.GetHmacKey64(db.getHmacKey(), Types.ULONG_MAX_VALUE), "HmacSHA256"); - hmac.init(signingKey); - } catch (NoSuchAlgorithmException e) { - throw new PwDbOutputException(e); - } catch (InvalidKeyException e) { - throw new PwDbOutputException(e); - } - - dos = new DigestOutputStream(os, md); - mos = new MacOutputStream(dos, hmac); - los = new LEDataOutputStream(mos); - } - - public void output() throws IOException { - - los.writeUInt(PwDbHeader.PWM_DBSIG_1); - los.writeUInt(PwDbHeaderV4.DBSIG_2); - los.writeUInt(header.getVersion()); - - - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CipherID, Types.UUIDtoBytes(db.getDataCipher())); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.CompressionFlags, LEDataOutputStream.writeIntBuf(db.getCompressionAlgorithm().getId())); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.MasterSeed, header.getMasterSeed()); - - if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformSeed, header.getTransformSeed()); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.TransformRounds, LEDataOutputStream.writeLongBuf(db.getNumberKeyEncryptionRounds())); - } else { - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.KdfParameters, KdfParameters.serialize(db.getKdfParameters())); - } - - if (header.getEncryptionIV().length > 0) { - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EncryptionIV, header.getEncryptionIV()); - } - - if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomstreamKey, header.innerRandomStreamKey); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.StreamStartBytes, header.streamStartBytes); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.InnerRandomStreamID, LEDataOutputStream.writeIntBuf(header.innerRandomStream.getId())); - } - - if (db.containsPublicCustomData()) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - LEDataOutputStream los = new LEDataOutputStream(bos); - VariantDictionary.serialize(db.getPublicCustomData(), los); - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.PublicCustomData, bos.toByteArray()); - } - - writeHeaderField(PwDbHeaderV4.PwDbHeaderV4Fields.EndOfHeader, EndHeaderValue); - - los.flush(); - hashOfHeader = dos.getMessageDigest().digest(); - headerHmac = mos.getMac(); - } - - private void writeHeaderField(byte fieldId, byte[] pbData) throws IOException { - // Write the field id - los.write(fieldId); - - if (pbData != null) { - writeHeaderFieldSize(pbData.length); - los.write(pbData); - } else { - writeHeaderFieldSize(0); - } - } - - private void writeHeaderFieldSize(int size) throws IOException { - if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - los.writeUShort(size); - } else { - los.writeInt(size); - } - - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java deleted file mode 100644 index 851b72987..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbInnerHeaderOutputV4.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 Brian Pellin. - * - * This file is part of KeePass DX. - * - * KeePassDroid is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePassDroid is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePassDroid. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.database.element.PwDatabaseV4; -import com.kunzisoft.keepass.database.element.PwDbHeaderV4; -import com.kunzisoft.keepass.database.security.ProtectedBinary; -import com.kunzisoft.keepass.stream.LEDataOutputStream; -import com.kunzisoft.keepass.utils.MemUtil; - -import java.io.IOException; -import java.io.OutputStream; - -public class PwDbInnerHeaderOutputV4 { - private PwDatabaseV4 db; - private PwDbHeaderV4 header; - private LEDataOutputStream los; - - public PwDbInnerHeaderOutputV4(PwDatabaseV4 db, PwDbHeaderV4 header, OutputStream os) { - this.db = db; - this.header = header; - - this.los = new LEDataOutputStream(os); - } - - public void output() throws IOException { - los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomStreamID); - los.writeInt(4); - los.writeInt(header.innerRandomStream.getId()); - - int streamKeySize = header.innerRandomStreamKey.length; - los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.InnerRandomstreamKey); - los.writeInt(streamKeySize); - los.write(header.innerRandomStreamKey); - - for (ProtectedBinary protectedBinary : db.getBinPool().binaries()) { - byte flag = PwDbHeaderV4.KdbxBinaryFlags.None; - if (protectedBinary.isProtected()) { - flag |= PwDbHeaderV4.KdbxBinaryFlags.Protected; - } - - los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.Binary); - los.writeInt((int) protectedBinary.length() + 1); // TODO verify - los.write(flag); - - MemUtil.readBytes(protectedBinary.getData(), - buffer -> los.write(buffer)); - } - - los.write(PwDbHeaderV4.PwDbInnerHeaderV4Fields.EndOfHeader); - los.writeInt(0); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java deleted file mode 100644 index 6b8d5ab89..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbOutput.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.database.element.PwDbHeader; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; - -import java.io.OutputStream; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -public abstract class PwDbOutput
{ - - protected OutputStream mOS; - - protected PwDbOutput(OutputStream os) { - mOS = os; - } - - protected SecureRandom setIVs(Header header) throws PwDbOutputException { - SecureRandom random; - try { - random = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - throw new PwDbOutputException("Does not support secure random number generation."); - } - random.nextBytes(header.getEncryptionIV()); - random.nextBytes(header.getMasterSeed()); - - return random; - } - - public abstract void output() throws PwDbOutputException; - - public abstract Header outputHeader(OutputStream os) throws PwDbOutputException; - -} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java deleted file mode 100644 index b8cda3f05..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV3Output.java +++ /dev/null @@ -1,278 +0,0 @@ -/* -` * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.database.element.*; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; -import com.kunzisoft.keepass.stream.LEDataOutputStream; -import com.kunzisoft.keepass.stream.NullOutputStream; - -import javax.crypto.Cipher; -import javax.crypto.CipherOutputStream; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.*; -import java.util.ArrayList; -import java.util.List; - -public class PwDbV3Output extends PwDbOutput { - - private PwDatabaseV3 mDatabaseV3; - private byte[] headerHashBlock; - - public PwDbV3Output(PwDatabaseV3 pm, OutputStream os) { - super(os); - mDatabaseV3 = pm; - } - - public byte[] getFinalKey(PwDbHeader header) throws PwDbOutputException { - try { - PwDbHeaderV3 h3 = (PwDbHeaderV3) header; - mDatabaseV3.makeFinalKey(h3.getMasterSeed(), h3.transformSeed, mDatabaseV3.getNumberKeyEncryptionRounds()); - return mDatabaseV3.getFinalKey(); - } catch (IOException e) { - throw new PwDbOutputException("Key creation failed.", e); - } - } - - @Override - public void output() throws PwDbOutputException { - // 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 - sortGroupsForOutput(); - - PwDbHeader header = outputHeader(mOS); - - byte[] finalKey = getFinalKey(header); - - Cipher cipher; - try { - if (mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AESRijndael) { - cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); - } else if (mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish){ - cipher = CipherFactory.getInstance("Twofish/CBC/PKCS7PADDING"); - } else { - throw new Exception(); - } - } catch (Exception e) { - throw new PwDbOutputException("Algorithm not supported.", e); - } - - try { - cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(finalKey, "AES" ), new IvParameterSpec(header.getEncryptionIV()) ); - CipherOutputStream cos = new CipherOutputStream(mOS, cipher); - BufferedOutputStream bos = new BufferedOutputStream(cos); - outputPlanGroupAndEntries(bos); - bos.flush(); - bos.close(); - - } catch (InvalidKeyException e) { - throw new PwDbOutputException("Invalid key", e); - } catch (InvalidAlgorithmParameterException e) { - throw new PwDbOutputException("Invalid algorithm parameter.", e); - } catch (IOException e) { - throw new PwDbOutputException("Failed to output final encrypted part.", e); - } - } - - @Override - protected SecureRandom setIVs(PwDbHeaderV3 header) throws PwDbOutputException { - SecureRandom random = super.setIVs(header); - random.nextBytes(header.transformSeed); - return random; - } - - @Override - public PwDbHeaderV3 outputHeader(OutputStream os) throws PwDbOutputException { - // Build header - PwDbHeaderV3 header = new PwDbHeaderV3(); - header.signature1 = PwDbHeader.PWM_DBSIG_1; - header.signature2 = PwDbHeaderV3.DBSIG_2; - header.flags = PwDbHeaderV3.FLAG_SHA2; - - if ( mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.AESRijndael) { - header.flags |= PwDbHeaderV3.FLAG_RIJNDAEL; - } else if ( mDatabaseV3.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish ) { - header.flags |= PwDbHeaderV3.FLAG_TWOFISH; - } else { - throw new PwDbOutputException("Unsupported algorithm."); - } - - header.version = PwDbHeaderV3.DBVER_DW; - header.numGroups = mDatabaseV3.numberOfGroups(); - header.numEntries = mDatabaseV3.numberOfEntries(); - header.numKeyEncRounds = (int) mDatabaseV3.getNumberKeyEncryptionRounds(); - - setIVs(header); - - // Content checksum - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new PwDbOutputException("SHA-256 not implemented here.", e); - } - - // Header checksum - MessageDigest headerDigest; - try { - headerDigest = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new PwDbOutputException("SHA-256 not implemented here.", e); - } - NullOutputStream nos; - nos = new NullOutputStream(); - DigestOutputStream headerDos = new DigestOutputStream(nos, headerDigest); - - // Output header for the purpose of calculating the header checksum - PwDbHeaderOutputV3 pho = new PwDbHeaderOutputV3(header, headerDos); - try { - pho.outputStart(); - pho.outputEnd(); - headerDos.flush(); - } catch (IOException e) { - throw new PwDbOutputException(e); - } - byte[] headerHash = headerDigest.digest(); - headerHashBlock = getHeaderHashBuffer(headerHash); - - // Output database for the purpose of calculating the content checksum - nos = new NullOutputStream(); - DigestOutputStream dos = new DigestOutputStream(nos, md); - BufferedOutputStream bos = new BufferedOutputStream(dos); - try { - outputPlanGroupAndEntries(bos); - bos.flush(); - bos.close(); - } catch (IOException e) { - throw new PwDbOutputException("Failed to generate checksum.", e); - } - - header.contentsHash = md.digest(); - - // Output header for real output, containing content hash - pho = new PwDbHeaderOutputV3(header, os); - try { - pho.outputStart(); - dos.on(false); - pho.outputContentHash(); - dos.on(true); - pho.outputEnd(); - dos.flush(); - } catch (IOException e) { - throw new PwDbOutputException(e); - } - - return header; - } - - public void outputPlanGroupAndEntries(OutputStream os) throws PwDbOutputException { - LEDataOutputStream los = new LEDataOutputStream(os); - - if (useHeaderHash() && headerHashBlock != null) { - try { - los.writeUShort(0x0000); - los.writeInt(headerHashBlock.length); - los.write(headerHashBlock); - } catch (IOException e) { - throw new PwDbOutputException("Failed to output header hash.", e); - } - } - - // Groups - for (PwGroupV3 group: mDatabaseV3.getGroupIndexes()) { - PwGroupOutputV3 pgo = new PwGroupOutputV3(group, os); - try { - pgo.output(); - } catch (IOException e) { - throw new PwDbOutputException("Failed to output a tree", e); - } - } - - // Entries - for (PwEntryV3 entry : mDatabaseV3.getEntryIndexes()) { - PwEntryOutputV3 peo = new PwEntryOutputV3(entry, os); - try { - peo.output(); - } catch (IOException e) { - throw new PwDbOutputException("Failed to output an entry.", e); - } - } - } - - private void sortGroupsForOutput() { - List groupList = new ArrayList<>(); - // Rebuild list according to coalation sorting order removing any orphaned groups - for (PwGroupV3 rootGroup : mDatabaseV3.getRootGroups()) { - sortGroup(rootGroup, groupList); - } - mDatabaseV3.setGroupIndexes(groupList); - } - - private void sortGroup(PwGroupV3 group, List groupList) { - // Add current tree - groupList.add(group); - - // Recurse over children - for (PwGroupV3 childGroup : group.getChildGroups()) { - sortGroup(childGroup, groupList); - } - } - - private byte[] getHeaderHashBuffer(byte[] headerDigest) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - writeExtData(headerDigest, baos); - return baos.toByteArray(); - } catch (IOException e) { - return null; - } - } - - private void writeExtData(byte[] headerDigest, OutputStream os) throws IOException { - LEDataOutputStream los = new LEDataOutputStream(os); - - writeExtDataField(los, 0x0001, headerDigest, headerDigest.length); - byte[] headerRandom = new byte[32]; - SecureRandom rand = new SecureRandom(); - rand.nextBytes(headerRandom); - writeExtDataField(los, 0x0002, headerRandom, headerRandom.length); - writeExtDataField(los, 0xFFFF, null, 0); - - } - - private void writeExtDataField(LEDataOutputStream los, int fieldType, byte[] data, int fieldSize) throws IOException { - los.writeUShort(fieldType); - los.writeInt(fieldSize); - if (data != null) { - los.write(data); - } - - } - - protected boolean useHeaderHash() { - return true; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java deleted file mode 100644 index 1b9e6f8f1..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwDbV4Output.java +++ /dev/null @@ -1,765 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import android.util.Log; -import android.util.Xml; -import biz.source_code.base64Coder.Base64Coder; -import com.kunzisoft.keepass.crypto.CipherFactory; -import com.kunzisoft.keepass.crypto.PwStreamCipherFactory; -import com.kunzisoft.keepass.crypto.engine.CipherEngine; -import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine; -import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory; -import com.kunzisoft.keepass.database.*; -import com.kunzisoft.keepass.database.element.*; -import com.kunzisoft.keepass.database.exception.PwDbOutputException; -import com.kunzisoft.keepass.database.exception.UnknownKDF; -import com.kunzisoft.keepass.database.security.ProtectedBinary; -import com.kunzisoft.keepass.database.security.ProtectedString; -import com.kunzisoft.keepass.stream.HashedBlockOutputStream; -import com.kunzisoft.keepass.stream.HmacBlockOutputStream; -import com.kunzisoft.keepass.stream.LEDataOutputStream; -import com.kunzisoft.keepass.utils.DateUtil; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.MemUtil; -import com.kunzisoft.keepass.utils.Types; -import org.joda.time.DateTime; -import org.spongycastle.crypto.StreamCipher; -import org.xmlpull.v1.XmlSerializer; - -import javax.crypto.Cipher; -import javax.crypto.CipherOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.*; -import java.util.Map.Entry; -import java.util.zip.GZIPOutputStream; - -public class PwDbV4Output extends PwDbOutput { - private static final String TAG = PwDbV4Output.class.getName(); - - private PwDatabaseV4 mPM; - private StreamCipher randomStream; - private XmlSerializer xml; - private PwDbHeaderV4 header; - private byte[] hashOfHeader; - private byte[] headerHmac; - private CipherEngine engine = null; - - public PwDbV4Output(PwDatabaseV4 pm, OutputStream os) { - super(os); - this.mPM = pm; - } - - @Override - public void output() throws PwDbOutputException { - - try { - try { - engine = CipherFactory.getInstance(mPM.getDataCipher()); - } catch (NoSuchAlgorithmException e) { - throw new PwDbOutputException("No such cipher", e); - } - - header = outputHeader(mOS); - - OutputStream osPlain; - if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - CipherOutputStream cos = attachStreamEncryptor(header, mOS); - cos.write(header.streamStartBytes); - - osPlain = new HashedBlockOutputStream(cos); - } else { - mOS.write(hashOfHeader); - mOS.write(headerHmac); - - HmacBlockOutputStream hbos = new HmacBlockOutputStream(mOS, mPM.getHmacKey()); - osPlain = attachStreamEncryptor(header, hbos); - } - - OutputStream osXml; - try { - if (mPM.getCompressionAlgorithm() == PwCompressionAlgorithm.Gzip) { - osXml = new GZIPOutputStream(osPlain); - } else { - osXml = osPlain; - } - - if (header.getVersion() >= PwDbHeaderV4.FILE_VERSION_32_4) { - PwDbInnerHeaderOutputV4 ihOut = new PwDbInnerHeaderOutputV4(mPM, header, osXml); - ihOut.output(); - } - - outputDatabase(osXml); - osXml.close(); - } catch (IllegalArgumentException e) { - throw new PwDbOutputException(e); - } catch (IllegalStateException e) { - throw new PwDbOutputException(e); - } - } catch (IOException e) { - throw new PwDbOutputException(e); - } - } - - private void outputDatabase(OutputStream os) throws IllegalArgumentException, IllegalStateException, IOException { - - xml = Xml.newSerializer(); - - xml.setOutput(os, "UTF-8"); - xml.startDocument("UTF-8", true); - - xml.startTag(null, PwDatabaseV4XML.ElemDocNode); - - writeMeta(); - - PwGroupV4 root = mPM.getRootGroup(); - xml.startTag(null, PwDatabaseV4XML.ElemRoot); - startGroup(root); - Stack groupStack = new Stack<>(); - groupStack.push(root); - - if (!root.doForEachChild( - new NodeHandler() { - @Override - public boolean operate(PwEntryV4 entry) { - try { - writeEntry(entry, false); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - - return true; - } - }, - new NodeHandler() { - @Override - public boolean operate(PwGroupV4 node) { - while (true) { - try { - if (node.getParent() == groupStack.peek()) { - groupStack.push(node); - startGroup(node); - break; - } else { - groupStack.pop(); - if (groupStack.size() <= 0) return false; - endGroup(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - return true; - } - })) throw new RuntimeException("Writing groups failed"); - - while (groupStack.size() > 1) { - xml.endTag(null, PwDatabaseV4XML.ElemGroup); - groupStack.pop(); - } - - endGroup(); - - writeList(PwDatabaseV4XML.ElemDeletedObjects, mPM.getDeletedObjects()); - - xml.endTag(null, PwDatabaseV4XML.ElemRoot); - - xml.endTag(null, PwDatabaseV4XML.ElemDocNode); - xml.endDocument(); - - } - - private void writeMeta() throws IllegalArgumentException, IllegalStateException, IOException { - xml.startTag(null, PwDatabaseV4XML.ElemMeta); - - writeObject(PwDatabaseV4XML.ElemGenerator, mPM.localizedAppName); - - if (hashOfHeader != null) { - writeObject(PwDatabaseV4XML.ElemHeaderHash, String.valueOf(Base64Coder.encode(hashOfHeader))); - } - - writeObject(PwDatabaseV4XML.ElemDbName, mPM.getName(), true); - writeObject(PwDatabaseV4XML.ElemDbNameChanged, mPM.getNameChanged().getDate()); - writeObject(PwDatabaseV4XML.ElemDbDesc, mPM.getDescription(), true); - writeObject(PwDatabaseV4XML.ElemDbDescChanged, mPM.getDescriptionChanged().getDate()); - writeObject(PwDatabaseV4XML.ElemDbDefaultUser, mPM.getDefaultUserName(), true); - writeObject(PwDatabaseV4XML.ElemDbDefaultUserChanged, mPM.getDefaultUserNameChanged().getDate()); - writeObject(PwDatabaseV4XML.ElemDbMntncHistoryDays, mPM.getMaintenanceHistoryDays()); - writeObject(PwDatabaseV4XML.ElemDbColor, mPM.getColor()); - writeObject(PwDatabaseV4XML.ElemDbKeyChanged, mPM.getKeyLastChanged().getDate()); - writeObject(PwDatabaseV4XML.ElemDbKeyChangeRec, mPM.getKeyChangeRecDays()); - writeObject(PwDatabaseV4XML.ElemDbKeyChangeForce, mPM.getKeyChangeForceDays()); - - writeList(PwDatabaseV4XML.ElemMemoryProt, mPM.getMemoryProtection()); - - writeCustomIconList(); - - writeObject(PwDatabaseV4XML.ElemRecycleBinEnabled, mPM.isRecycleBinEnabled()); - writeObject(PwDatabaseV4XML.ElemRecycleBinUuid, mPM.getRecycleBinUUID()); - writeObject(PwDatabaseV4XML.ElemRecycleBinChanged, mPM.getRecycleBinChanged()); - writeObject(PwDatabaseV4XML.ElemEntryTemplatesGroup, mPM.getEntryTemplatesGroup()); - writeObject(PwDatabaseV4XML.ElemEntryTemplatesGroupChanged, mPM.getEntryTemplatesGroupChanged().getDate()); - writeObject(PwDatabaseV4XML.ElemHistoryMaxItems, mPM.getHistoryMaxItems()); - writeObject(PwDatabaseV4XML.ElemHistoryMaxSize, mPM.getHistoryMaxSize()); - writeObject(PwDatabaseV4XML.ElemLastSelectedGroup, mPM.getLastSelectedGroup()); - writeObject(PwDatabaseV4XML.ElemLastTopVisibleGroup, mPM.getLastTopVisibleGroup()); - - if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - writeBinPool(); - } - writeList(PwDatabaseV4XML.ElemCustomData, mPM.getCustomData()); - - xml.endTag(null, PwDatabaseV4XML.ElemMeta); - - } - - private CipherOutputStream attachStreamEncryptor(PwDbHeaderV4 header, OutputStream os) throws PwDbOutputException { - Cipher cipher; - try { - //mPM.makeFinalKey(header.masterSeed, mPM.kdfParameters); - - cipher = engine.getCipher(Cipher.ENCRYPT_MODE, mPM.getFinalKey(), header.getEncryptionIV()); - } catch (Exception e) { - throw new PwDbOutputException("Invalid algorithm.", e); - } - - return new CipherOutputStream(os, cipher); - } - - @Override - protected SecureRandom setIVs(PwDbHeaderV4 header) throws PwDbOutputException { - SecureRandom random = super.setIVs(header); - random.nextBytes(header.getMasterSeed()); - - int ivLength = engine.ivLength(); - if (ivLength != header.getEncryptionIV().length) { - header.setEncryptionIV(new byte[ivLength]); - } - random.nextBytes(header.getEncryptionIV()); - - if (mPM.getKdfParameters() == null) { - mPM.setKdfParameters(KdfFactory.aesKdf.getDefaultParameters()); - } - - try { - KdfEngine kdf = KdfFactory.getEngineV4(mPM.getKdfParameters()); - kdf.randomize(mPM.getKdfParameters()); - } catch (UnknownKDF unknownKDF) { - Log.e(TAG, "Unable to retrieve header", unknownKDF); - } - - if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - header.innerRandomStream = CrsAlgorithm.Salsa20; - header.innerRandomStreamKey = new byte[32]; - } else { - header.innerRandomStream = CrsAlgorithm.ChaCha20; - header.innerRandomStreamKey = new byte[64]; - } - random.nextBytes(header.innerRandomStreamKey); - - randomStream = PwStreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey); - if (randomStream == null) { - throw new PwDbOutputException("Invalid random cipher"); - } - - if ( header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - random.nextBytes(header.streamStartBytes); - } - - return random; - } - - @Override - public PwDbHeaderV4 outputHeader(OutputStream os) throws PwDbOutputException { - - PwDbHeaderV4 header = new PwDbHeaderV4(mPM); - setIVs(header); - - PwDbHeaderOutputV4 pho = new PwDbHeaderOutputV4(mPM, header, os); - try { - pho.output(); - } catch (IOException e) { - throw new PwDbOutputException("Failed to output the header.", e); - } - - hashOfHeader = pho.getHashOfHeader(); - headerHmac = pho.headerHmac; - - return header; - } - - private void startGroup(PwGroupV4 group) throws IllegalArgumentException, IllegalStateException, IOException { - xml.startTag(null, PwDatabaseV4XML.ElemGroup); - writeObject(PwDatabaseV4XML.ElemUuid, group.getId()); - writeObject(PwDatabaseV4XML.ElemName, group.getTitle()); - writeObject(PwDatabaseV4XML.ElemNotes, group.getNotes()); - writeObject(PwDatabaseV4XML.ElemIcon, group.getIcon().getIconId()); - - if (!group.getIconCustom().equals(PwIconCustom.Companion.getZERO())) { - writeObject(PwDatabaseV4XML.ElemCustomIconID, group.getIconCustom().getUuid()); - } - - writeList(PwDatabaseV4XML.ElemTimes, group); - writeObject(PwDatabaseV4XML.ElemIsExpanded, group.isExpanded()); - writeObject(PwDatabaseV4XML.ElemGroupDefaultAutoTypeSeq, group.getDefaultAutoTypeSequence()); - writeObject(PwDatabaseV4XML.ElemEnableAutoType, group.getEnableAutoType()); - writeObject(PwDatabaseV4XML.ElemEnableSearching, group.getEnableSearching()); - writeObject(PwDatabaseV4XML.ElemLastTopVisibleEntry, group.getLastTopVisibleEntry()); - - } - - private void endGroup() throws IllegalArgumentException, IllegalStateException, IOException { - xml.endTag(null, PwDatabaseV4XML.ElemGroup); - } - - private void writeEntry(PwEntryV4 entry, boolean isHistory) throws IllegalArgumentException, IllegalStateException, IOException { - assert(entry != null); - - xml.startTag(null, PwDatabaseV4XML.ElemEntry); - - writeObject(PwDatabaseV4XML.ElemUuid, entry.getId()); - writeObject(PwDatabaseV4XML.ElemIcon, entry.getIcon().getIconId()); - - if (!entry.getIconCustom().equals(PwIconCustom.Companion.getZERO())) { - writeObject(PwDatabaseV4XML.ElemCustomIconID, entry.getIconCustom().getUuid()); - } - - writeObject(PwDatabaseV4XML.ElemFgColor, entry.getForegroundColor()); - writeObject(PwDatabaseV4XML.ElemBgColor, entry.getBackgroundColor()); - writeObject(PwDatabaseV4XML.ElemOverrideUrl, entry.getOverrideURL()); - writeObject(PwDatabaseV4XML.ElemTags, entry.getTags()); - - writeList(PwDatabaseV4XML.ElemTimes, entry); - - writeList(entry.getFields().getListOfAllFields(), true); - writeList(entry.getBinaries()); - writeList(PwDatabaseV4XML.ElemAutoType, entry.getAutoType()); - - if (!isHistory) { - writeList(PwDatabaseV4XML.ElemHistory, entry.getHistory(), true); - } - // else entry.sizeOfHistory() == 0 - - xml.endTag(null, PwDatabaseV4XML.ElemEntry); - } - - - private void writeObject(String key, ProtectedBinary value) throws IllegalArgumentException, IllegalStateException, IOException { - assert(key != null && value != null); - - xml.startTag(null, PwDatabaseV4XML.ElemBinary); - xml.startTag(null, PwDatabaseV4XML.ElemKey); - xml.text(safeXmlString(key)); - xml.endTag(null, PwDatabaseV4XML.ElemKey); - - xml.startTag(null, PwDatabaseV4XML.ElemValue); - int ref = mPM.getBinPool().findKey(value); - String strRef = Integer.toString(ref); - - if (strRef != null) { - xml.attribute(null, PwDatabaseV4XML.AttrRef, strRef); - } - else { - subWriteValue(value); - } - xml.endTag(null, PwDatabaseV4XML.ElemValue); - - xml.endTag(null, PwDatabaseV4XML.ElemBinary); - } - - /* - TODO Make with pipe - private void subWriteValue(ProtectedBinary value) throws IllegalArgumentException, IllegalStateException, IOException { - try (InputStream inputStream = value.getData()) { - if (inputStream == null) { - Log.e(TAG, "Can't write a null input stream."); - return; - } - - if (value.isProtected()) { - xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue); - - try (InputStream cypherInputStream = - IOUtil.pipe(inputStream, - o -> new org.spongycastle.crypto.io.CipherOutputStream(o, randomStream))) { - writeInputStreamInBase64(cypherInputStream); - } - - } else { - if (mPM.getCompressionAlgorithm() == PwCompressionAlgorithm.Gzip) { - - xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue); - - try (InputStream gZipInputStream = - IOUtil.pipe(inputStream, GZIPOutputStream::new, (int) value.length())) { - writeInputStreamInBase64(gZipInputStream); - } - - } else { - writeInputStreamInBase64(inputStream); - } - } - } - } - - private void writeInputStreamInBase64(InputStream inputStream) throws IOException { - try (InputStream base64InputStream = - IOUtil.pipe(inputStream, - o -> new Base64OutputStream(o, DEFAULT))) { - MemUtil.readBytes(base64InputStream, - buffer -> xml.text(Arrays.toString(buffer))); - } - } - //*/ - - //* - private void subWriteValue(ProtectedBinary value) throws IllegalArgumentException, IllegalStateException, IOException { - - int valLength = (int) value.length(); - if (valLength > 0) { - byte[] buffer = new byte[valLength]; - if (valLength == value.getData().read(buffer, 0, valLength)) { - - if (value.isProtected()) { - xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue); - - byte[] encoded = new byte[valLength]; - randomStream.processBytes(buffer, 0, valLength, encoded, 0); - xml.text(String.valueOf(Base64Coder.encode(encoded))); - - } else { - if (mPM.getCompressionAlgorithm() == PwCompressionAlgorithm.Gzip) { - xml.attribute(null, PwDatabaseV4XML.AttrCompressed, PwDatabaseV4XML.ValTrue); - - byte[] compressData = MemUtil.compress(buffer); - xml.text(String.valueOf(Base64Coder.encode(compressData))); - - } else { - xml.text(String.valueOf(Base64Coder.encode(buffer))); - } - } - } else { - Log.e(TAG, "Unable to read the stream of the protected binary"); - } - } - } - //*/ - - private void writeObject(String name, String value, boolean filterXmlChars) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && value != null); - - xml.startTag(null, name); - - if (filterXmlChars) { - value = safeXmlString(value); - } - - xml.text(value); - xml.endTag(null, name); - } - - private void writeObject(String name, String value) throws IllegalArgumentException, IllegalStateException, IOException { - writeObject(name, value, false); - } - - private void writeObject(String name, Date value) throws IllegalArgumentException, IllegalStateException, IOException { - if (header.getVersion() < PwDbHeaderV4.FILE_VERSION_32_4) { - writeObject(name, PwDatabaseV4XML.dateFormatter.get().format(value)); - } else { - DateTime dt = new DateTime(value); - long seconds = DateUtil.convertDateToKDBX4Time(dt); - byte[] buf = LEDataOutputStream.writeLongBuf(seconds); - String b64 = new String(Base64Coder.encode(buf)); - writeObject(name, b64); - } - - } - - private void writeObject(String name, long value) throws IllegalArgumentException, IllegalStateException, IOException { - writeObject(name, String.valueOf(value)); - } - - private void writeObject(String name, Boolean value) throws IllegalArgumentException, IllegalStateException, IOException { - String text; - if (value == null) { - text = "null"; - } - else if (value) { - text = PwDatabaseV4XML.ValTrue; - } - else { - text = PwDatabaseV4XML.ValFalse; - } - - writeObject(name, text); - } - - private void writeObject(String name, UUID uuid) throws IllegalArgumentException, IllegalStateException, IOException { - byte[] data = Types.UUIDtoBytes(uuid); - writeObject(name, String.valueOf(Base64Coder.encode(data))); - } - - private void writeObject(String name, String keyName, String keyValue, String valueName, String valueValue) throws IllegalArgumentException, IllegalStateException, IOException { - xml.startTag(null, name); - - xml.startTag(null, keyName); - xml.text(safeXmlString(keyValue)); - xml.endTag(null, keyName); - - xml.startTag(null, valueName); - xml.text(safeXmlString(valueValue)); - xml.endTag(null, valueName); - - xml.endTag(null, name); - } - - private void writeList(String name, AutoType autoType) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && autoType != null); - - xml.startTag(null, name); - - writeObject(PwDatabaseV4XML.ElemAutoTypeEnabled, autoType.getEnabled()); - writeObject(PwDatabaseV4XML.ElemAutoTypeObfuscation, autoType.getObfuscationOptions()); - - if (autoType.getDefaultSequence().length() > 0) { - writeObject(PwDatabaseV4XML.ElemAutoTypeDefaultSeq, autoType.getDefaultSequence(), true); - } - - for (Entry pair : autoType.entrySet()) { - writeObject(PwDatabaseV4XML.ElemAutoTypeItem, PwDatabaseV4XML.ElemWindow, pair.getKey(), PwDatabaseV4XML.ElemKeystrokeSequence, pair.getValue()); - } - - xml.endTag(null, name); - - } - - private void writeList(Map strings, boolean isEntryString) throws IllegalArgumentException, IllegalStateException, IOException { - assert (strings != null); - - for (Entry pair : strings.entrySet()) { - writeObject(pair.getKey(), pair.getValue(), isEntryString); - - } - - } - - private void writeObject(String key, ProtectedString value, boolean isEntryString) throws IllegalArgumentException, IllegalStateException, IOException { - assert(key !=null && value != null); - - xml.startTag(null, PwDatabaseV4XML.ElemString); - xml.startTag(null, PwDatabaseV4XML.ElemKey); - xml.text(safeXmlString(key)); - xml.endTag(null, PwDatabaseV4XML.ElemKey); - - xml.startTag(null, PwDatabaseV4XML.ElemValue); - boolean protect = value.isProtected(); - if (isEntryString) { - if (key.equals(MemoryProtectionConfig.ProtectDefinition.TITLE_FIELD)) { - protect = mPM.getMemoryProtection().getProtectTitle(); - } - else if (key.equals(MemoryProtectionConfig.ProtectDefinition.USERNAME_FIELD)) { - protect = mPM.getMemoryProtection().getProtectUserName(); - } - else if (key.equals(MemoryProtectionConfig.ProtectDefinition.PASSWORD_FIELD)) { - protect = mPM.getMemoryProtection().getProtectPassword(); - } - else if (key.equals(MemoryProtectionConfig.ProtectDefinition.URL_FIELD)) { - protect = mPM.getMemoryProtection().getProtectUrl(); - } - else if (key.equals(MemoryProtectionConfig.ProtectDefinition.NOTES_FIELD)) { - protect = mPM.getMemoryProtection().getProtectNotes(); - } - } - - if (protect) { - xml.attribute(null, PwDatabaseV4XML.AttrProtected, PwDatabaseV4XML.ValTrue); - - byte[] data = value.toString().getBytes("UTF-8"); - int valLength = data.length; - - if (valLength > 0) { - byte[] encoded = new byte[valLength]; - randomStream.processBytes(data, 0, valLength, encoded, 0); - xml.text(String.valueOf(Base64Coder.encode(encoded))); - } - } - else { - xml.text(safeXmlString(value.toString())); - } - - xml.endTag(null, PwDatabaseV4XML.ElemValue); - xml.endTag(null, PwDatabaseV4XML.ElemString); - - } - - private void writeObject(String name, PwDeletedObject value) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && value != null); - - xml.startTag(null, name); - - writeObject(PwDatabaseV4XML.ElemUuid, value.getUuid()); - writeObject(PwDatabaseV4XML.ElemDeletionTime, value.getDeletionTime()); - - xml.endTag(null, name); - } - - private void writeList(Map binaries) throws IllegalArgumentException, IllegalStateException, IOException { - assert(binaries != null); - - for (Entry pair : binaries.entrySet()) { - writeObject(pair.getKey(), pair.getValue()); - } - } - - - private void writeList(String name, List value) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && value != null); - - xml.startTag(null, name); - - for (PwDeletedObject pdo : value) { - writeObject(PwDatabaseV4XML.ElemDeletedObject, pdo); - } - - xml.endTag(null, name); - - } - - private void writeList(String name, MemoryProtectionConfig value) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && value != null); - - xml.startTag(null, name); - - writeObject(PwDatabaseV4XML.ElemProtTitle, value.getProtectTitle()); - writeObject(PwDatabaseV4XML.ElemProtUserName, value.getProtectUserName()); - writeObject(PwDatabaseV4XML.ElemProtPassword, value.getProtectPassword()); - writeObject(PwDatabaseV4XML.ElemProtURL, value.getProtectUrl()); - writeObject(PwDatabaseV4XML.ElemProtNotes, value.getProtectNotes()); - - xml.endTag(null, name); - - } - - private void writeList(String name, Map customData) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && customData != null); - - xml.startTag(null, name); - - for (Entry pair : customData.entrySet()) { - writeObject(PwDatabaseV4XML.ElemStringDictExItem, PwDatabaseV4XML.ElemKey, pair.getKey(), PwDatabaseV4XML.ElemValue, pair.getValue()); - - } - - xml.endTag(null, name); - - } - - private void writeList(String name, NodeV4Interface it) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && it != null); - - xml.startTag(null, name); - - writeObject(PwDatabaseV4XML.ElemLastModTime, it.getLastModificationTime().getDate()); - writeObject(PwDatabaseV4XML.ElemCreationTime, it.getCreationTime().getDate()); - writeObject(PwDatabaseV4XML.ElemLastAccessTime, it.getLastAccessTime().getDate()); - writeObject(PwDatabaseV4XML.ElemExpiryTime, it.getExpiryTime().getDate()); - writeObject(PwDatabaseV4XML.ElemExpires, it.isExpires()); - writeObject(PwDatabaseV4XML.ElemUsageCount, it.getUsageCount()); - writeObject(PwDatabaseV4XML.ElemLocationChanged, it.getLocationChanged().getDate()); - - xml.endTag(null, name); - } - - private void writeList(String name, List value, boolean isHistory) throws IllegalArgumentException, IllegalStateException, IOException { - assert(name != null && value != null); - - xml.startTag(null, name); - - for (PwEntryV4 entry : value) { - writeEntry(entry, isHistory); - } - - xml.endTag(null, name); - - } - - private void writeCustomIconList() throws IllegalArgumentException, IllegalStateException, IOException { - List customIcons = mPM.getCustomIcons(); - if (customIcons.size() == 0) return; - - xml.startTag(null, PwDatabaseV4XML.ElemCustomIcons); - - for (PwIconCustom icon : customIcons) { - xml.startTag(null, PwDatabaseV4XML.ElemCustomIconItem); - - writeObject(PwDatabaseV4XML.ElemCustomIconItemID, icon.getUuid()); - writeObject(PwDatabaseV4XML.ElemCustomIconItemData, String.valueOf(Base64Coder.encode(icon.getImageData()))); - - xml.endTag(null, PwDatabaseV4XML.ElemCustomIconItem); - } - - xml.endTag(null, PwDatabaseV4XML.ElemCustomIcons); - } - - private void writeBinPool() throws IllegalArgumentException, IllegalStateException, IOException { - xml.startTag(null, PwDatabaseV4XML.ElemBinaries); - - for (Entry pair : mPM.getBinPool().entrySet()) { - xml.startTag(null, PwDatabaseV4XML.ElemBinary); - xml.attribute(null, PwDatabaseV4XML.AttrId, Integer.toString(pair.getKey())); - - subWriteValue(pair.getValue()); - - xml.endTag(null, PwDatabaseV4XML.ElemBinary); - - } - - xml.endTag(null, PwDatabaseV4XML.ElemBinaries); - - } - - private String safeXmlString(String text) { - if (EmptyUtils.isNullOrEmpty(text)) { - return text; - } - - StringBuilder sb = new StringBuilder(); - - char ch; - for (int i = 0; i < text.length(); i++) { - ch = text.charAt(i); - - if(((ch >= 0x20) && (ch <= 0xD7FF)) || - (ch == 0x9) || (ch == 0xA) || (ch == 0xD) || - ((ch >= 0xE000) && (ch <= 0xFFFD))) { - - sb.append(ch); - } - - } - - return sb.toString(); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java deleted file mode 100644 index 37f37590e..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwEntryOutputV3.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.database.element.PwEntryV3; -import com.kunzisoft.keepass.stream.LEDataOutputStream; -import com.kunzisoft.keepass.utils.Types; - -import java.io.IOException; -import java.io.OutputStream; - -public class PwEntryOutputV3 { - // Constants - public static final byte[] UUID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(1); - public static final byte[] GROUPID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(2); - public static final byte[] IMAGEID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(3); - public static final byte[] TITLE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(4); - public static final byte[] URL_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(5); - public static final byte[] USERNAME_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(6); - public static final byte[] PASSWORD_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(7); - public static final byte[] ADDITIONAL_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(8); - public static final byte[] CREATE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(9); - public static final byte[] MOD_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(10); - public static final byte[] ACCESS_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(11); - public static final byte[] EXPIRE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(12); - public static final byte[] BINARY_DESC_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(13); - public static final byte[] BINARY_DATA_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(14); - public static final byte[] END_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(0xFFFF); - public static final byte[] LONG_FOUR = LEDataOutputStream.writeIntBuf(4); - public static final byte[] UUID_FIELD_SIZE = LEDataOutputStream.writeIntBuf(16); - public static final byte[] DATE_FIELD_SIZE = LEDataOutputStream.writeIntBuf(5); - public static final byte[] IMAGEID_FIELD_SIZE = LONG_FOUR; - public static final byte[] LEVEL_FIELD_SIZE = LONG_FOUR; - public static final byte[] FLAGS_FIELD_SIZE = LONG_FOUR; - public static final byte[] ZERO_FIELD_SIZE = LEDataOutputStream.writeIntBuf(0); - public static final byte[] ZERO_FIVE = {0x00, 0x00, 0x00, 0x00, 0x00}; - public static final byte[] TEST = {0x33, 0x33, 0x33, 0x33}; - - private OutputStream mOS; - private PwEntryV3 mPE; - private long outputBytes = 0; - - /** Output the PwGroupV3 to the stream - * @param pe - * @param os - */ - public PwEntryOutputV3(PwEntryV3 pe, OutputStream os) { - mPE = pe; - mOS = os; - } - - //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int - public void output() throws IOException { - - outputBytes += 134; // Length of fixed size fields - - // UUID - mOS.write(UUID_FIELD_TYPE); - mOS.write(UUID_FIELD_SIZE); - mOS.write(Types.UUIDtoBytes(mPE.getId())); - - // Group ID - mOS.write(GROUPID_FIELD_TYPE); - mOS.write(LONG_FOUR); - mOS.write(LEDataOutputStream.writeIntBuf(mPE.getParent().getId())); - - // Image ID - mOS.write(IMAGEID_FIELD_TYPE); - mOS.write(LONG_FOUR); - mOS.write(LEDataOutputStream.writeIntBuf(mPE.getIcon().getIconId())); - - // Title - //byte[] title = mPE.title.getBytes("UTF-8"); - mOS.write(TITLE_FIELD_TYPE); - int titleLen = Types.writeCString(mPE.getTitle(), mOS); - outputBytes += titleLen; - - // URL - mOS.write(URL_FIELD_TYPE); - int urlLen = Types.writeCString(mPE.getUrl(), mOS); - outputBytes += urlLen; - - // Username - mOS.write(USERNAME_FIELD_TYPE); - int userLen = Types.writeCString(mPE.getUsername(), mOS); - outputBytes += userLen; - - // Password - byte[] password = mPE.getPasswordBytes(); - mOS.write(PASSWORD_FIELD_TYPE); - mOS.write(LEDataOutputStream.writeIntBuf(password.length+1)); - mOS.write(password); - mOS.write(0); - outputBytes += password.length + 1; - - // Additional - mOS.write(ADDITIONAL_FIELD_TYPE); - int addlLen = Types.writeCString(mPE.getNotes(), mOS); - outputBytes += addlLen; - - // Create date - writeDate(CREATE_FIELD_TYPE, mPE.getCreationTime().getCDate()); - - // Modification date - writeDate(MOD_FIELD_TYPE, mPE.getLastModificationTime().getCDate()); - - // Access date - writeDate(ACCESS_FIELD_TYPE, mPE.getLastAccessTime().getCDate()); - - // Expiration date - writeDate(EXPIRE_FIELD_TYPE, mPE.getExpiryTime().getCDate()); - - // Binary desc - mOS.write(BINARY_DESC_FIELD_TYPE); - int descLen = Types.writeCString(mPE.getBinaryDesc(), mOS); - outputBytes += descLen; - - // Binary data - int dataLen = writeByteArray(mPE.getBinaryData()); - outputBytes += dataLen; - - // End - mOS.write(END_FIELD_TYPE); - mOS.write(ZERO_FIELD_SIZE); - } - - private int writeByteArray(byte[] data) throws IOException { - int dataLen; - if ( data != null ) { - dataLen = data.length; - } else { - dataLen = 0; - } - mOS.write(BINARY_DATA_FIELD_TYPE); - mOS.write(LEDataOutputStream.writeIntBuf(dataLen)); - if ( data != null ) { - mOS.write(data); - } - - return dataLen; - } - - private void writeDate(byte[] type, byte[] date) throws IOException { - mOS.write(type); - mOS.write(DATE_FIELD_SIZE); - if ( date != null ) { - mOS.write(date); - } else { - mOS.write(ZERO_FIVE); - } - } - - /** Returns the number of bytes written by the stream - * @return Number of bytes written - */ - public long getLength() { - return outputBytes; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java b/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java deleted file mode 100644 index 0f3f2384e..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/save/PwGroupOutputV3.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.save; - -import com.kunzisoft.keepass.database.element.PwGroupV3; -import com.kunzisoft.keepass.stream.LEDataOutputStream; -import com.kunzisoft.keepass.utils.Types; - -import java.io.IOException; -import java.io.OutputStream; - -public class PwGroupOutputV3 { - // Constants - public static final byte[] GROUPID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(1); - public static final byte[] NAME_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(2); - public static final byte[] CREATE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(3); - public static final byte[] MOD_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(4); - public static final byte[] ACCESS_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(5); - public static final byte[] EXPIRE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(6); - public static final byte[] IMAGEID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(7); - public static final byte[] LEVEL_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(8); - public static final byte[] FLAGS_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(9); - public static final byte[] END_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(0xFFFF); - public static final byte[] LONG_FOUR = LEDataOutputStream.writeIntBuf(4); - public static final byte[] GROUPID_FIELD_SIZE = LONG_FOUR; - public static final byte[] DATE_FIELD_SIZE = LEDataOutputStream.writeIntBuf(5); - public static final byte[] IMAGEID_FIELD_SIZE = LONG_FOUR; - public static final byte[] LEVEL_FIELD_SIZE = LEDataOutputStream.writeIntBuf(2); - public static final byte[] FLAGS_FIELD_SIZE = LONG_FOUR; - public static final byte[] ZERO_FIELD_SIZE = LEDataOutputStream.writeIntBuf(0); - - private OutputStream mOS; - private PwGroupV3 mPG; - - /** Output the PwGroupV3 to the stream - * @param pg - * @param os - */ - public PwGroupOutputV3(PwGroupV3 pg, OutputStream os) { - mPG = pg; - mOS = os; - } - - public void output() throws IOException { - //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int, but most values can't be greater than 2^31, so it probably doesn't matter. - - // Group ID - mOS.write(GROUPID_FIELD_TYPE); - mOS.write(GROUPID_FIELD_SIZE); - mOS.write(LEDataOutputStream.writeIntBuf(mPG.getId())); - - // Name - mOS.write(NAME_FIELD_TYPE); - Types.writeCString(mPG.getTitle(), mOS); - - // Create date - mOS.write(CREATE_FIELD_TYPE); - mOS.write(DATE_FIELD_SIZE); - mOS.write(mPG.getCreationTime().getCDate()); - - // Modification date - mOS.write(MOD_FIELD_TYPE); - mOS.write(DATE_FIELD_SIZE); - mOS.write(mPG.getLastModificationTime().getCDate()); - - // Access date - mOS.write(ACCESS_FIELD_TYPE); - mOS.write(DATE_FIELD_SIZE); - mOS.write(mPG.getLastAccessTime().getCDate()); - - // Expiration date - mOS.write(EXPIRE_FIELD_TYPE); - mOS.write(DATE_FIELD_SIZE); - mOS.write(mPG.getExpiryTime().getCDate()); - - // Image ID - mOS.write(IMAGEID_FIELD_TYPE); - mOS.write(IMAGEID_FIELD_SIZE); - mOS.write(LEDataOutputStream.writeIntBuf(mPG.getIcon().getIconId())); - - // Level - mOS.write(LEVEL_FIELD_TYPE); - mOS.write(LEVEL_FIELD_SIZE); - mOS.write(LEDataOutputStream.writeUShortBuf(mPG.getLevel())); - - // Flags - mOS.write(FLAGS_FIELD_TYPE); - mOS.write(FLAGS_FIELD_SIZE); - mOS.write(LEDataOutputStream.writeIntBuf(mPG.getFlags())); - - // End - mOS.write(END_FIELD_TYPE); - mOS.write(ZERO_FIELD_SIZE); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt index 4b5d3b41b..c649098f7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/EntrySearchHandlerV4.kt @@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.search import com.kunzisoft.keepass.database.NodeHandler import com.kunzisoft.keepass.database.element.PwEntryV4 -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4 +import com.kunzisoft.keepass.database.search.iterator.EntrySearchStringIteratorV4 import com.kunzisoft.keepass.utils.StringUtil import com.kunzisoft.keepass.utils.UuidUtil diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt index a963eae38..ee3445b34 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt @@ -23,9 +23,9 @@ import com.kunzisoft.keepass.database.NodeHandler import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.EntryVersioned import com.kunzisoft.keepass.database.element.GroupVersioned -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIterator -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV3 -import com.kunzisoft.keepass.database.iterator.EntrySearchStringIteratorV4 +import com.kunzisoft.keepass.database.search.iterator.EntrySearchStringIterator +import com.kunzisoft.keepass.database.search.iterator.EntrySearchStringIteratorV3 +import com.kunzisoft.keepass.database.search.iterator.EntrySearchStringIteratorV4 import java.util.* class SearchDbHelper(private val isOmitBackup: Boolean) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIterator.kt similarity index 93% rename from app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt rename to app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIterator.kt index 43c6bb9a1..d98d5a65e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIterator.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIterator.kt @@ -17,6 +17,6 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.iterator +package com.kunzisoft.keepass.database.search.iterator abstract class EntrySearchStringIterator : Iterator \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorV3.kt similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.kt rename to app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorV3.kt index 2ff81e61b..cf786054d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorV3.kt @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.iterator +package com.kunzisoft.keepass.database.search.iterator import com.kunzisoft.keepass.database.element.PwEntryV3 import com.kunzisoft.keepass.database.search.SearchParameters diff --git a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorV4.kt similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt rename to app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorV4.kt index 3886040fb..71f7453e2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/iterator/EntrySearchStringIteratorV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorV4.kt @@ -17,11 +17,11 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.database.iterator +package com.kunzisoft.keepass.database.search.iterator import com.kunzisoft.keepass.database.element.PwEntryV4 import com.kunzisoft.keepass.database.search.SearchParametersV4 -import com.kunzisoft.keepass.database.security.ProtectedString +import com.kunzisoft.keepass.database.element.security.ProtectedString import java.util.* import kotlin.collections.Map.Entry diff --git a/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java b/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java deleted file mode 100644 index 91ba1e415..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedBinary.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.security; - -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; - -public class ProtectedBinary implements Parcelable { - - private static final String TAG = ProtectedBinary.class.getName(); - - private boolean protect; - private byte[] data; - private File dataFile; - private int size; - - public boolean isProtected() { - return protect; - } - - public long length() { - if (data != null) - return data.length; - if (dataFile != null) - return size; - return 0; - } - - /** - * Empty protected binary - */ - public ProtectedBinary() { - this.protect = false; - this.data = null; - this.dataFile = null; - this.size = 0; - } - - public ProtectedBinary(ProtectedBinary protectedBinary) { - this.protect = protectedBinary.protect; - this.data = protectedBinary.data; - this.dataFile = protectedBinary.dataFile; - this.size = protectedBinary.size; - } - - public ProtectedBinary(boolean enableProtection, byte[] data) { - this.protect = enableProtection; - this.data = data; - this.dataFile = null; - if (data != null) - this.size = data.length; - else - this.size = 0; - } - - public ProtectedBinary(boolean enableProtection, File dataFile, int size) { - this.protect = enableProtection; - this.data = null; - this.dataFile = dataFile; - this.size = size; - } - - private ProtectedBinary(Parcel in) { - protect = in.readByte() != 0; - in.readByteArray(data); - dataFile = new File(in.readString()); - size = in.readInt(); - } - - public InputStream getData() throws IOException { - if (data != null) - return new ByteArrayInputStream(data); - else if (dataFile != null) - return new FileInputStream(dataFile); - else - return null; - } - - public void clear() { - data = null; - if (dataFile != null && !dataFile.delete()) - Log.e(TAG, "Unable to delete temp file " + dataFile.getAbsolutePath()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ProtectedBinary that = (ProtectedBinary) o; - return protect == that.protect && - size == that.size && - Arrays.equals(data, that.data) && - dataFile != null && - dataFile.equals(that.dataFile); - } - - @Override - public int hashCode() { - - int result = 0; - result = 31 * result + (protect ? 1 : 0); - result = 31 * result + dataFile.hashCode(); - result = 31 * result + Integer.valueOf(size).hashCode(); - result = 31 * result + Arrays.hashCode(data); - return result; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeByte((byte) (protect ? 1 : 0)); - dest.writeByteArray(data); - dest.writeString(dataFile.getAbsolutePath()); - dest.writeInt(size); - } - - public static final Creator CREATOR = new Creator() { - @Override - public ProtectedBinary createFromParcel(Parcel in) { - return new ProtectedBinary(in); - } - - @Override - public ProtectedBinary[] newArray(int size) { - return new ProtectedBinary[size]; - } - }; - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedString.java b/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedString.java deleted file mode 100644 index c0decee4a..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/security/ProtectedString.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.security; - -import android.os.Parcel; -import android.os.Parcelable; - -public class ProtectedString implements Parcelable { - - private boolean protect; - private String string; - - public ProtectedString() { - this(false, ""); - } - - public ProtectedString(ProtectedString toCopy) { - this.protect = toCopy.protect; - this.string = toCopy.string; - } - - public ProtectedString(boolean enableProtection, String string) { - this.protect = enableProtection; - this.string = string; - } - - public ProtectedString(Parcel in) { - protect = in.readByte() != 0; - string = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeByte((byte) (protect ? 1 : 0)); - dest.writeString(string); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public ProtectedString createFromParcel(Parcel in) { - return new ProtectedString(in); - } - - @Override - public ProtectedString[] newArray(int size) { - return new ProtectedString[size]; - } - }; - - public boolean isProtected() { - return protect; - } - - public int length() { - if (string == null) { - return 0; - } - - return string.length(); - } - - @Override - public String toString() { - return string; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java index 6da530de5..9eeea705d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java @@ -33,7 +33,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.utils.Util; import java.text.DateFormat; diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.java index ecdafd6ef..56491e61d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.java +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomField.java @@ -28,7 +28,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.utils.Util; public class EntryCustomField extends LinearLayout { diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.java index 117b100ef..59c353a6c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.java +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryCustomFieldProtected.java @@ -23,7 +23,7 @@ import android.content.Context; import android.text.method.PasswordTransformationMethod; import android.util.AttributeSet; -import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.database.element.security.ProtectedString; public class EntryCustomFieldProtected extends EntryCustomField{ diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.java index 85a7a8094..2a3ef0bcb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.java +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.java @@ -31,7 +31,7 @@ import android.widget.RelativeLayout; import android.widget.TextView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.security.ProtectedString; +import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.utils.Util; public class EntryEditCustomField extends RelativeLayout { From 32f170f644a47ffecaceffc318574e5f7038c657 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 10 Jun 2019 14:40:08 +0200 Subject: [PATCH 119/289] Remove unused import --- .../keepass/database/action/AssignPasswordInDatabaseRunnable.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt index 90f0d3326..9bddc68c7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt @@ -24,7 +24,6 @@ import android.net.Uri import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.InvalidKeyFileException import com.kunzisoft.keepass.tasks.ActionRunnable -import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.getUriInputStream import java.io.IOException From 776660923bbe3dbed1d7bdc880a9add2e9cff07a Mon Sep 17 00:00:00 2001 From: GiulioEl Date: Sat, 8 Jun 2019 20:30:43 +0000 Subject: [PATCH 120/289] Translated using Weblate (Italian) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/ --- app/src/main/res/values-it/strings.xml | 51 +++++++++++--------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 569641f9f..59f93d03f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -167,30 +167,28 @@ Problema impronta: %1$s Usa l\'impronta per salvare questa password Questo database non ha ancora alcuna password. - Inserisci una password e/o file chiave per sbloccare il database. \n \nRicorda di salvare una copia del tuo file .kdbx in un luogo sicuro dopo ogni modifica. - - 5 secondi - 10 secondi - 20 secondi - 30 secondi - 1 minuto - 5 minuti - 15 minuti - 30 minuti - Mai + 5 secondi + 10 secondi + 20 secondi + 30 secondi + 1 minuto + 5 minuti + 15 minuti + 30 minuti + Mai - Piccolo - Medio - Grande + Piccolo + Medio + Grande -Non mostrare di nuovo + Non mostrare di nuovo Consenti - Copia di %1$s + Copiato %1$s Cifratura Funzione di derivazione chiave ASCII esteso @@ -286,7 +284,6 @@ Testo App Altro - Tastiera Magitastiera Attiva una tastiera personale che popola le tue password e i campi di identità @@ -299,12 +296,10 @@ Cambia tastiera premendo a lungo la barra spaziatrice, oppure, se non disponibile, con: Blocca il database. Usa di nuovo la tastiera predefinita. - Permetti password mancante Attiva il pulsante \"Apri\" se non è selezionata l\'identificazione della password Sola lettura Apri il database in sola lettura in modo predefinito - Schermate educative Evidenzia gli elementi per imparare come funziona l\'app Ripristina schermate educative @@ -347,36 +342,32 @@ Scegli l\'ordine di elementi e gruppi. Partecipa Aiuta a migliorare la stabilità, la sicurezza e ad aggiungere nuove funzioni. - Diversamente da molte app di gestione password, questa è senza pubblicità, sofware libero (copyleft) e non raccoglie dati personali nei suoi server, non importa quale versione usi. - Acquistando la versione pro, avrai accesso a questa funzione visiva e soprattutto aiuterai nella realizzazione di progetti della comunità. + Acquistando la versione pro, avrai accesso a questa funzione visiva e soprattutto aiuterai nella realizzazione di progetti della comunità. + Questa funzione visiva è disponibile grazie alla tua generosità. - Per mantenere la nostra libertà ed essere sempre attivi, contiamo sul tuo contributo. - + Per mantenere la nostra libertà ed essere sempre attivi, contiamo sul tuo contributo. + Questa funzione è in sviluppo e richiede il tuo contributo per essere disponibile a breve. Acquistando la versione pro, - Contribuendo, + + Contribuendo, incoraggi gli sviluppatori a creare nuove funzioni e a correggere errori secondo le tue osservazioni. Grazie mille per il tuo contributo. Stiamo lavorando sodo per rilasciare questa funzione a breve. Non dimenticare di tenere aggiornata l\'app installando nuove versioni. - Scarica Contribuisci - Rijndael (AES) Twofish ChaCha20 - AES KDF Argon2 - Tema app Tema usato nell\'app Pacchetto icone Pacchetto di icone usato nell\'app - -Seleziona un elemento con la chiave. + Seleziona un elemento con la chiave. Riempi i campi usando gli elementi giusti. Modifica elemento Caricamento del database fallito. From 3f0071ac58641f4682121b42ef05c47cd3415527 Mon Sep 17 00:00:00 2001 From: Yaron Shahrabani Date: Sun, 16 Jun 2019 12:34:44 +0000 Subject: [PATCH 121/289] Translated using Weblate (Hebrew) Currently translated at 21.2% (73 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/he/ --- app/src/main/res/values-iw/strings.xml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index c6fc37067..ab905c785 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1,4 +1,4 @@ - + - - - משוב: - דף הבית: +--> + משוב + דף הבית KeePass DX היא תוכנה המממשת את מנהל הסיסמאות KeePass לאנרואיד. קבל הוסף ערך @@ -149,9 +147,7 @@ רישית כרטיס ה-SD במצב לקריאה בלבד. אתה לא תוכל לשמור או ליצור במסד הנתונים. גרסה %1$s - הזן סיסמה ו/או קובץ מפתח כדי לפתוח את מסד הנתונים. - 5 שניות 10 שניות @@ -168,11 +164,11 @@ בינוני גדול -ערוך רשומה + ערוך רשומה הוסף מחרוזת הצפנה אפשר החלק לניקוי לוח העתקה עכשיו כתובת URL לא ניתן לטעון את בסיס הנתונים - + \ No newline at end of file From 23bd6e20a45c247dbf495153334daa4cde2dba79 Mon Sep 17 00:00:00 2001 From: Noel Date: Sun, 23 Jun 2019 14:47:17 +0000 Subject: [PATCH 122/289] Translated using Weblate (Spanish) Currently translated at 49.0% (169 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/ --- app/src/main/res/values-es/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 259bd6a68..b267564b2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -346,4 +346,6 @@ Spanish translation by José I. Paños. Updated by David García-Abad (23-09-201 Protegido contra escritura Modificable Compila %1$s + Si la supresión automática del portapapeles falla, borran el historial manualmente. + Ajustes de Magikeyboard \ No newline at end of file From 8f0fe8957955bfe6cc337eadd4df89244d757488 Mon Sep 17 00:00:00 2001 From: CurlingTongs Date: Thu, 27 Jun 2019 11:31:46 +0000 Subject: [PATCH 123/289] Translated using Weblate (German) Currently translated at 99.7% (344 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 169 ++++++++++++------------- 1 file changed, 84 insertions(+), 85 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index cd8019cc7..8c45c424a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -44,7 +44,7 @@ Zwischenablagesperre Dauer der Speicherung in der Zwischenablage %1$s in die Zwischenablage kopieren - Datenbank-Schlüsseldatei erzeugen\u2026 + Datenbank-Schlüsseldatei erzeugen … Datenbank Entschlüsselung der Datenbankinhalte … Als Standard-Datenbank benutzen @@ -73,21 +73,21 @@ Dateinamen eingeben. Konnte Datei nicht erstellen: Datenbank nicht lesbar. - Stellen Sie sicher, dass der Pfad korrekt ist. + Sicherstellen, dass der Pfad korrekt ist. Namen eingeben. Schlüsseldatei wählen. Zu wenig Speicherplatz, um die ganze Datenbank zu laden. Mindestens eine Art der Passwortgenerierung muss ausgewählt werden. Die Passwörter stimmen nicht überein. - Mache \"Transformationsrunden\" zu einer Zahl. - \"Transformationsrunden\" zu hoch. Stelle ein auf 214748364848. + „Transformationsrunden“ zu einer Zahl machen. + „Transformationsrunden“ zu hoch. Wird auf 214748364848 eingestellt. Für jede Zeichenfolge ist ein Feldname notwendig. Titel hinzufügen. - Geben Sie die erforderliche Länge als positive, ganze Zahl in das Feld ein. + Eine positive ganze Zahl in das Feld „Länge“ eingeben. Feldname Feldwert Datei nicht gefunden. - Datei nicht gefunden. Versuchen Sie, sie über Ihren Dateimanger zu öffnen. + Datei nicht gefunden. Bitte versuchen, sie über den Dateimanager zu öffnen. Dateimanager Passwort generieren Passwort wiederholen @@ -107,12 +107,12 @@ Länge Gruppenliste Schriftgröße der Gruppenliste - Datenbank wird geladen\u2026 + Datenbank wird geladen … Kleinbuchstaben Passwort verstecken Passwörter standardmäßig mit (***) maskieren Über - Master-Schlüssel ändern + Hauptschlüssel ändern Einstellungen Datenbank-Einstellungen Löschen @@ -127,16 +127,16 @@ Bindestrich Nie Keine Suchergebnisse - Installieren Sie einen Web-Browser, um diese URL zu öffnen. + Bitte einen Webbrowser installieren, um diese URL zu öffnen. Zuletzt geöffnete Datenbanken Papierkorb/Sicherungen nicht durchsuchen Sicherungseinträge werden bei der Suche nicht berücksichtigt (nur bei .kdb Dateien) - Neue Datenbank anlegen\u2026 - Ausführen\u2026 + Neue Datenbank anlegen … + Ausführen … Sicherheit - Schreibgeschutzt - KeePass DX benötigt Schreibrechte, um etwas an Ihrer Datenbank zu ändern. - Ab Android KitKat haben auf einigen Geräten Apps keine Schreibrechte mehr für die SD-Karte. + Schreibgeschützt + KeePass DX benötigt Schreibrechte, um etwas an der Datenbank zu ändern. + Ab Android KitKat haben Apps auf einigen Geräten keine Schreibrechte mehr für die SD-Karte. Zuletzt verwendete Datenbanken Zuletzt verwendete Datenbanken merken Erinnert sich an den Speicherort der Schlüsseldateien der Datenbanken @@ -147,7 +147,7 @@ Schlüsseltransformationen Zusätzliche Schlüsseltransformationen bieten einen besseren Schutz gegen Wörterbuch- oder Brute-Force-Angriffe. Allerdings dauert dann auch das Laden und Speichern der Datenbank entsprechend länger. Schlüsseltransformationen - Speichere Datenbank\u2026 + Datenbank wird gespeichert … Leerzeichen Suchen Natürliche Datenbank @@ -161,13 +161,13 @@ Storage Access Framework (SAF) als Dateimanager verwenden (Android KitKat und später) Speicherzugriff-Framework Warnung - Vermeiden Sie Passwortzeichen, die nicht Teil des Latin-1-Zeichensatzes sind, in .kbd-Dateien, da sie alle in ein und dasselbe Zeichen umgewandelt werden. - Gewähren Sie Schreibzugriff auf die SD-Karte, um Datenbankänderungen speichern zu können. - Hängen Sie die SD-Karte ein, um eine Datenbank erstellen oder laden zu können. + Passwortzeichen in .kbd-Dateien vermeiden, die nicht Teil des Latin-1-Zeichensatzes sind, da sie alle in ein und dasselbe Zeichen umgewandelt werden. + Schreibzugriff auf die SD-Karte gewähren, um Datenbankänderungen speichern zu können. + Die SD-Karte einbinden, um eine Datenbank erstellen oder laden zu können. Version %1$s - Geben Sie ein Passwort und/oder eine Schlüsseldatei zum Entsperren Ihrer Datenbank ein. -\n -\nDenken Sie daran, nach jeder Änderung eine Kopie Ihrer .kdbx-Datei an einem sicheren Ort abzuspeichern. + Das Passwort und/oder eine Schlüsseldatei zum Entsperren der Datenbank eingeben. +\n +\nDaran denken, nach jeder Änderung die .kdbx-Datei an einem sicheren Ort abzuspeichern. 5 Sekunden 10 Sekunden @@ -184,13 +184,13 @@ Mittel Groß - Sind Sie sicher, dass das Entsperren ohne Passwort möglich sein soll\? - Sind Sie sicher, dass Sie keinen Verschlüsselungsschlüssel verwenden wollen ? + Soll das Entsperren ohne Passwort wirklich möglich sein\? + Soll wirklich kein Verschlüsselungsschlüssel verwendet werden\? Aussehen Generierte Passwortlänge Legt die Standardlänge des generierten Passworts fest Zwischenablagenbenachrichtigungen - Zwischenablagenbenachrichtigungen einschalten um Eingabefelder zu aktivieren + Zwischenablagenbenachrichtigungen einschalten, um Eingabefelder zu aktivieren Bildschirmsperre Datenbank sperren, wenn der Bildschirm ausgeschaltet wird Neue Datenbank erstellen @@ -199,29 +199,29 @@ Pfad Dateiname Dieses Feature konnte nicht gestartet werden. - Ermöglicht die Datenbanköffnung mit Ihrem Fingerabdruck + Ermöglicht die Datenbanköffnung mit dem Fingerabdruck Fingerabdruck Fingerabdruckscanner Fingerabdruck scannen, um die Datenbank zu öffnen, wenn die Passworteingabe ausgeschaltet ist. - Fingerabdruck scannen, um Ihr Datenbank-Passwort sicher zu speichern. - Geben Sie Ihr Datenbank-Passwort ein + Fingerabdruck scannen, um das Datenbank-Passwort sicher zu speichern. + Datenbank-Passwort eingeben Sperre Erlaubte Zeichen für Passwortgenerator festlegen Passwortzeichen Fingerabdruck-Scannen wird unterstützt, ist jedoch nicht konfiguriert. Fingerabdruck wird gescannt Verschlüsseltes Passwort wurde gespeichert - Fingerabdruckschlüssel nicht lesbar. Stellen Sie Ihr Passwort wieder her. + Fingerabdruckschlüssel nicht lesbar. Bitte das Passwort wiederherstellen. Problem mit dem Fingerabdruck: %1$s Verlauf Wie richte ich den Fingerabdruckscanner für schnelles Entsperren ein? - Speichern Sie Ihren für Ihr Gerät eingelesenen Fingerabdruck in - \"Einstellungen\" → \"Sicherheit\" → \"Fingerabdruck\" + Eingelesenen Fingerabdruck für das Gerät speichern in + „Einstellungen“ → „Sicherheit“ → „Fingerabdruck“ Verwendung Allgemein - Fingerabdruck verwenden um dieses Passwort zu speichern + Fingerabdruck verwenden, um dieses Passwort zu speichern Diese Datenbank hat noch kein Passwort. - App-Design, welches in der App genutzt wird + App-Design, das in der App genutzt wird Verschlüsselung Schlüsselableitungsfunktion Erweitertes ASCII @@ -232,11 +232,11 @@ Formularausfüllung Gespeicherten Fingerabdruck löschen Verschlüsselungsalgorithmus der Datenbank wird für sämtliche Daten verwendet. - Um den Schlüssel für den Verschlüsselungsalgorithmus zu generieren, wird der Masterschlüssel umgewandelt, wobei ein zufälliger Salt in der Schlüsselberechnung verwendet wird. + Um den Schlüssel für den Verschlüsselungsalgorithmus zu generieren, wird der Hauptschlüssel umgewandelt, wobei ein zufälliger Salt in der Schlüsselberechnung verwendet wird. Speichernutzung Größe des Speichers (in binären Bytes) der für die Schlüsselableitung genutzt wird. Parallelismus - Grad des Parallelismus (d.h. Anzahl der Threads) der für die Schlüsselableitung genutzt wird. + Grad des Parallelismus (d. h. Anzahl der Threads), der für die Schlüsselableitung genutzt wird. Sortieren Aufsteigend ↓ Gruppen davor @@ -253,10 +253,10 @@ Standard Autofill-Dienst auswählen Autofill aktivieren, um automatisch Eingabefelder in anderen Apps auszufüllen Zwischenablage - Verschlüsselungskeys löschen + Verschlüsselungsschlüssel löschen Alle Verschlüsselungsschlüssel für Fingerabdruckerkennung löschen - Sind Sie sicher, dass Sie alle mit der Fingerabdruckerkennung verknüpften Schlüssel löschen möchten\? - Ihre Android-Version, %1$s, erfüllt nicht die Mindestanforderung, Version %2$s. + Sollen wirklich alle mit der Fingerabdruckerkennung verknüpften Schlüssel gelöscht werden\? + Die Android-Version, %1$s, erfüllt nicht die Mindestanforderung für Version %2$s. Keine entsprechende Hardware. Bytes Dateipfad @@ -285,42 +285,41 @@ Magikeyboard Eine eigene Tastatur zum einfachen Ausfüllen aller Passwort- und Identitätsfelder aktivieren Hilfe-Anzeige wiederholen - Alle Hilfe-Themen noch einmal anzeigen + Alle Hilfethemen noch einmal anzeigen Hilfe-Anzeige zurückgesetzt - Ihre Datenbankdatei erstellen - Erstellen Sie Ihre erste Datei zur Passwortverwaltung. + Die Datenbankdatei erstellen + Die erste Datei zur Passwortverwaltung erstellen. Existierende Datenbank öffnen - Öffnen Sie eine frühere Datenbankdatei über Ihren Dateimanager, um sie weiter zu verwenden. - Ein Link zum Speicherort Ihrer Datei ist ausreichend - Sie können Ihre Datenbank auch mit einem physischen Link öffnen (z.B. mit file:// und content://). + Eine frühere Datenbankdatei über den Dateimanager öffnen, um sie weiter zu verwenden. + Ein Link zum Speicherort der Datei ist ausreichend + Die Datenbank lässt sich auch mit einem physischen Link öffnen (z. B. mit file:// und content://). Datenbankelemente hinzufügen - Einträge um Ihre digitalen Identitäten zu verwalten. -\n -\nGruppen (wie Ordner) um Einträge in ihrer Datenbank zu ordnen. + Einträge helfen, die digitalen Identitäten zu verwalten. +\n +\nGruppen (wie Ordner) helfen, Einträge in der Datenbank zu ordnen. Einträge durchsuchen - Titel, Benutzernamen oder Inhalte anderer Feldern eingeben um Ihre Passwörter wiederzufinden. + Titel, Benutzernamen oder Inhalte anderer Feldern eingeben, um die Passwörter wiederzufinden. Datenbank mit Fingerabdruck entsperren - Verknüpfen Sie Ihr Passwort und Ihren Fingerabdruck um Ihre Datenbank schnell zu entsperren. + Passwort und eigenen Fingerabdruck verknüpfen, um die Datenbank schnell zu entsperren. Eintrag bearbeiten - Bearbeiten Sie Ihren Eintrag mit benutzerdefinierten Feldern. Pooldaten können zwischen verschiedenen Eingabefeldern referenziert werden. - Ein starkes Passwort für ihren Eintrag erstellen. - Generieren Sie ein sicheres Passwort, um es mit Ihrem Eintrag zu verknüpfen. Definieren Sie es einfach nach den Kriterien des Formulars und vergessen Sie das sichere Passwort nicht. + Einträge mit benutzerdefinierten Feldern bearbeiten. Pooldaten können zwischen verschiedenen Eingabefeldern referenziert werden. + Ein starkes Passwort für den Eintrag erstellen. + Ein sicheres Passwort generieren, um es mit dem Eintrag zu verknüpfen. Einfach nach den Kriterien des Formulars definiert und das sichere Passwort nie vergessen. Benutzerdefinierte Felder hinzufügen - Registrieren Sie ein einfaches, nicht mitgeliefertes Feld, indem Sie ein neues Feld ausfüllen, das Sie auch schützen können. - Ihre Datenbank entsperren + Ein einfaches, nicht mitgeliefertes Feld registrieren, indem ein neues Feld ausfüllt wird, das sich auch schützen lässt. + Die Datenbank entsperren Ein Feld kopieren - Kopieren Sie einfach ein Feld, um es, wo Sie wollen, wieder einzufügen -\n -\nSie können verschiedene Methoden zum Ausfüllen von Formularen verwenden. Wählen Sie die, die Ihnen am meisten zusagt. + Kopierte Felder können an beliebiger Stelle eingefügt werden. +\n +\nAus den verschiedene Methoden zum Ausfüllen von Formularen einfach die bevorzugte auswählen. Datenbank sperren - Schnell Ihre Datenbank sperren, in den App-Einstellungen können Sie eine Sperre nach Zeit und bei Ausschalten des Bildschirms festlegen. - Sortierung der Elemente + Schnell die Datenbank sperren, in den App-Einstellungen lässt sich eine Sperre nach Zeit und bei Ausschalten des Bildschirms festlegen. + Sortierung der Einträge Auswählen, wie Einträge und Gruppen sortiert werden. Mitmachen - Machen Sie mit, damit sich Stabilität, Sicherheit verbessern und weitere Funktionen hinzukommen. - Anders als viele andere Passwortmanager ist dieser werbefrei, quelloffen und speichert keine persönlichen Daten auf dessen Servern - unabhängig von der Version, die Sie nutzen. - Mit dem Kauf der Pro-Version erhalten Sie Zugang zu dieser visuellen Funktion und Sie unterstützen insbesondere die Umsetzung gemeinschaftlicher Projektarbeiten. - + Mitmachen, um Stabilität und Sicherheit verbessern und weitere Funktionen zu ermöglichen. + Anders als viele andere Passwortmanager ist dieser werbefrei, quelloffen und speichert keine persönlichen Daten auf dessen Servern – unabhängig von der verwendeten Version. + Mit dem Kauf der Pro-Version erhält man Zugang zu dieser visuellen Funktion und unterstützt insbesondere die Umsetzung gemeinschaftlicher Projektarbeiten. Diese visuelle Funktion wurde wegen Ihrer Großzügigkeit freigeschaltet. Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren Beitrag. @@ -336,42 +335,42 @@ ChaCha20 AES KDF Argon2 - Icon-Paket - In der App verwendetes Icon-Packet + Symbolepaket + In der App verwendetes Symbolepaket Eine Gruppe kann nicht in sich selbst verschoben werden. Kopieren Verschieben Einfügen Abbrechen - Wenn die automatische Löschung der Zwischenablage fehlschlägt, löschen Sie ihren Verlauf manuell. + Wenn das automatische Löschen der Zwischenablage fehlschlägt, bitte den Verlauf manuell löschen. WARNUNG: Alle Apps teilen sich die Zwischenablage. Wenn sensible Daten kopiert werden, kann andere Software darauf zugreifen. Magikeyboard-Einstellungen Tastatur zum sicheren Ausfüllen von Formularen einrichten. - Aktivieren Sie das \"Magikeyboard\" in den Geräteeinstellungen. - \"Einstellungen\" → \"Sprache & Eingabe\" → \"Aktuelle Tastatur\" und auswählen. - oder (\"Einstellungen\" → \"Sprachen & Eingabe\" → \"Bildschirmtastatur\" und auswählen.) - Wählen Sie das Magikeyboard aus, wenn Sie ein Formular ausfüllen müssen. - Wechseln Sie die Tastaturen durch einen langen Druck auf die Leertaste oder, wenn das nicht zur Verfügung steht, mit: - Wählen Sie den Eintrag mit dem Schlüssel aus. - Füllen Sie die Felder mit den Elementen des Eintrags aus. - Sperren Sie die Datenbank. - Kehren Sie zur Standardtastatur zurück. + Das „Magikeyboard“ in den Geräteeinstellungen aktivieren. + „Einstellungen“ → „Sprache & Eingabe“ → „Aktuelle Tastatur“ und auswählen. + oder („Einstellungen“ → „Sprache & Eingabe“ → „Bildschirmtastatur“ und auswählen.) + Das Magikeyboard auswählen, wenn ein Formular ausgefüllt werden soll. + Tastaturen durch langes Drücken auf die Leertaste wechseln oder, wenn das nicht zur Verfügung steht, mit: + Den Eintrag mit dem Schlüssel auswählen. + Die Felder mit den Elementen des Eintrags ausfüllen. + Die Datenbank sperren. + Zur Standardtastatur zurückkehren. Kein Passwort zulassen - \"Öffnen\"-Taste aktivieren, wenn keine Passwort-Identifikation festgelegt ist + „Öffnen“-Taste aktivieren, wenn keine Passwort-Identifikation festgelegt ist Hilfe-Anzeige Bedienelemente hervorheben, um die Funktionsweise der App zu lernen - veränderbar - schreibgeschützt + Änderbar + Schreibgeschützt Schreibgeschützt Schreibschutz aktivieren Datenbank standardmäßig schreibgeschützt öffnen - Ändern Sie den Öffnungsmodus für die Sitzung. -\n -\n\"Write-protected\" verhindert unbeabsichtigte Änderungen an der Datenbank. -\nMit \"Modifizierbar\" können Sie alle Elemente hinzufügen, löschen oder ändern. + Den Öffnungsmodus für die Sitzung ändern. +\n +\n„Schreibgeschützt“ verhindert unbeabsichtigte Änderungen an der Datenbank. +\nMit „Änderbar“ können alle Elemente hinzugefügt, gelöscht oder geändert werden. Eintrag bearbeiten Datenbank kann nicht geladen werden. - Laden des Schlüssels fehlgeschlagen; versuchen Sie, die \"Speicherplatznutzung\" von KDF zu verringen. + Laden des Schlüssels fehlgeschlagen. Bitte versuchen, die „Speicherplatznutzung“ von KDF zu verringern. Benutzernamen anzeigen Benutzernamen in Eintragslisten anzeigen Build %1$s @@ -387,10 +386,10 @@ %1$s über Magikeyboard abrufbar %1$s Beim Schließen löschen - Die Tastatureingabe löschen, wenn die Benachrichtung geschlossen wird + Die Tastatureingabe löschen, wenn die Benachrichtigung geschlossen wird Aussehen Tastaturdesign Tasten - Vibration auf Tastendruck - Ton auf Tastendruck + Vibration bei Tastendruck + Ton bei Tastendruck \ No newline at end of file From ce05bb467efde89cd1c62753520cb520cd27dc01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 27 Jun 2019 03:54:53 +0000 Subject: [PATCH 124/289] Translated using Weblate (English) Currently translated at 100.0% (345 of 345 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 57a1788f3..2e242b6cf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -297,7 +297,7 @@ Timeout Timeout to clear the keyboard entry notification_entry_key - Notification information + Notification info Show a notification when an entry is available true Entry From 4905c262ba54959f8df0aa0c03aab86556625f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 27 Jun 2019 03:56:14 +0000 Subject: [PATCH 125/289] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 99.7% (344 of 345 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 2beff808e..27c48c04c 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -268,7 +268,7 @@ Tillat inget passord Skru på \"Åpne\"-tasten hvis ingen passordidentifikasjon er valgt. Skrivebeskyttet - Åpne din database skrivebeskyttet som forvalg. + Åpne din database skrivebeskyttet som forvalg Hjelpeskjermer Framhev elementer for opplæring i programmet Tilbakestill opplæringsskjermer From ab01d01fcebc68d0fdc5c2c026b455cc73a8a451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=2E=20R=C3=BCdinger?= Date: Fri, 28 Jun 2019 17:16:25 +0000 Subject: [PATCH 126/289] Translated using Weblate (German) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/de/ --- app/src/main/res/values-de/strings.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8c45c424a..0d3f2bbc0 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -306,7 +306,7 @@ Ein starkes Passwort für den Eintrag erstellen. Ein sicheres Passwort generieren, um es mit dem Eintrag zu verknüpfen. Einfach nach den Kriterien des Formulars definiert und das sichere Passwort nie vergessen. Benutzerdefinierte Felder hinzufügen - Ein einfaches, nicht mitgeliefertes Feld registrieren, indem ein neues Feld ausfüllt wird, das sich auch schützen lässt. + Ein wichtiges, nicht mitgeliefertes Feld erfassen, indem ein neues Feld ausgefüllt wird, das sich auch schützen lässt. Die Datenbank entsperren Ein Feld kopieren Kopierte Felder können an beliebiger Stelle eingefügt werden. @@ -317,9 +317,10 @@ Sortierung der Einträge Auswählen, wie Einträge und Gruppen sortiert werden. Mitmachen - Mitmachen, um Stabilität und Sicherheit verbessern und weitere Funktionen zu ermöglichen. + Mithelfen, um Stabilität und Sicherheit zu verbessern und weitere Funktionen zu ermöglichen. Anders als viele andere Passwortmanager ist dieser werbefrei, quelloffen und speichert keine persönlichen Daten auf dessen Servern – unabhängig von der verwendeten Version. - Mit dem Kauf der Pro-Version erhält man Zugang zu dieser visuellen Funktion und unterstützt insbesondere die Umsetzung gemeinschaftlicher Projektarbeiten. + Mit dem Kauf der Pro-Version erhält man Zugang zu dieser visuellen Funktion und unterstützt insbesondere die Umsetzung gemeinschaftlicher Projektarbeiten. + Diese visuelle Funktion wurde wegen Ihrer Großzügigkeit freigeschaltet. Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren Beitrag. From 49bb34b7425f0a09c2325d80944c7ea494cf0ea6 Mon Sep 17 00:00:00 2001 From: WaldiS Date: Wed, 3 Jul 2019 09:57:08 +0000 Subject: [PATCH 127/289] Translated using Weblate (Polish) Currently translated at 100.0% (345 of 345 strings) Translation: KeePass DX/Strings Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/ --- app/src/main/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index de35ce55d..824f580e7 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -377,7 +377,7 @@ along with KeePass DX. If not, see . Wpis Limit czasu Limit czasu, aby wyczyścić wpis klawiatury - Informacje dotyczące powiadomień + Informacje o powiadomieniach Pokaż powiadomienie, gdy wpis jest dostępny Wpis %1$s dostępne na Magikeyboard From e4b550afc19bd7593efcb7e919bf6796bf6de72a Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 4 Jul 2019 14:34:06 +0200 Subject: [PATCH 128/289] 4 spaces files harmonisation #184 --- .../kunzisoft/keepass/tests/AccentTest.java | 64 ++- .../kunzisoft/keepass/tests/OutputTests.java | 12 +- .../kunzisoft/keepass/tests/PwDateTest.java | 24 +- .../keepass/tests/PwEntryTestV3.java | 60 +- .../keepass/tests/PwEntryTestV4.java | 8 +- .../kunzisoft/keepass/tests/PwGroupTest.java | 28 +- .../com/kunzisoft/keepass/tests/TestUtil.java | 46 +- .../kunzisoft/keepass/tests/TypesTest.java | 332 +++++------ .../keepass/tests/crypto/AESTest.java | 120 ++-- .../keepass/tests/crypto/CipherTest.java | 134 ++--- .../keepass/tests/crypto/FinalKeyTest.java | 102 ++-- .../keepass/tests/database/DeleteEntry.java | 44 +- .../keepass/tests/database/EntryV4.java | 6 +- .../keepass/tests/database/Kdb3.java | 24 +- .../keepass/tests/database/Kdb3Twofish.java | 6 +- .../keepass/tests/database/Kdb4Header.java | 14 +- .../keepass/tests/database/SprEngineTest.java | 58 +- .../keepass/tests/database/TestData.java | 10 +- .../keepass/tests/search/SearchTest.java | 114 ++-- .../keepass/tests/stream/HashedBlock.java | 178 +++--- .../keepass/tests/utils/StringUtilTest.java | 44 +- .../java/com/kunzisoft/keepass/app/App.java | 76 +-- .../keepass/backup/SettingsBackupAgent.java | 22 +- .../kunzisoft/keepass/crypto/AESProvider.java | 18 +- .../keepass/crypto/CipherFactory.java | 102 ++-- .../keepass/crypto/NativeAESCipherSpi.java | 528 +++++++++--------- .../kunzisoft/keepass/crypto/NativeLib.java | 46 +- .../keepass/crypto/PwStreamCipherFactory.java | 68 +-- .../crypto/finalkey/AndroidFinalKey.java | 78 +-- .../keepass/crypto/finalkey/FinalKey.java | 4 +- .../crypto/finalkey/FinalKeyFactory.java | 28 +- .../crypto/finalkey/NativeFinalKey.java | 30 +- .../database/element/PwDatabaseV3.java | 330 +++++------ .../keepass/database/element/PwDate.java | 330 +++++------ .../dialogs/IconPickerDialogFragment.java | 136 ++--- .../keepass/dialogs/ReadOnlyDialog.java | 22 +- .../keepass/dialogs/WarningDialog.java | 86 +-- .../keepass/fileselect/BrowserDialog.java | 38 +- .../keepass/fileselect/FileDbHelper.java | 428 +++++++------- .../fileselect/FileSelectActivity.java | 484 ++++++++-------- .../keepass/icons/IconDrawableFactory.java | 196 +++---- .../magikeyboard/view/MagikeyboardView.java | 42 +- .../keepass/password/PasswordGenerator.java | 176 +++--- .../keepass/stream/CopyInputStream.java | 126 ++--- .../keepass/stream/CountInputStream.java | 94 ++-- .../keepass/stream/CountOutputStream.java | 60 +- .../stream/HashedBlockInputStream.java | 250 ++++----- .../stream/HashedBlockOutputStream.java | 206 +++---- .../keepass/stream/LEDataInputStream.java | 270 ++++----- .../keepass/stream/LEDataOutputStream.java | 204 +++---- .../keepass/stream/NullOutputStream.java | 40 +- .../stream/RandomFileOutputStream.java | 66 +-- .../kunzisoft/keepass/utils/EmptyUtils.java | 30 +- .../kunzisoft/keepass/utils/Interaction.java | 42 +- .../kunzisoft/keepass/utils/SprEngineV4.java | 356 ++++++------ .../com/kunzisoft/keepass/utils/Types.java | 228 ++++---- .../com/kunzisoft/keepass/utils/Util.java | 50 +- .../com/kunzisoft/keepass/utils/UuidUtil.java | 68 +-- 58 files changed, 3394 insertions(+), 3392 deletions(-) diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/AccentTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/AccentTest.java index 7aed9ad77..20b1a5d83 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/AccentTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/AccentTest.java @@ -1,22 +1,22 @@ /* -* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. -* -* This file is part of KeePass DX. -* -* KeePass DX is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* KeePass DX is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with KeePass DX. If not, see . -* -*/ + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ package com.kunzisoft.keepass.tests; import android.test.AndroidTestCase; @@ -24,19 +24,21 @@ import android.test.AndroidTestCase; import com.kunzisoft.keepass.tests.database.TestData; public class AccentTest extends AndroidTestCase { - - private static final String KEYFILE = ""; - private static final String PASSWORD = "é"; - private static final String ASSET = "accent.kdb"; - private static final String FILENAME = "/sdcard/accent.kdb"; - - public void testOpen() { - try { - TestData.GetDb(getContext(), ASSET, PASSWORD, KEYFILE, FILENAME); - } catch (Exception e) { - assertTrue("Failed to open database", false); - } - } + private static final String KEYFILE = ""; + private static final String PASSWORD = "é"; + private static final String ASSET = "accent.kdb"; + private static final String FILENAME = "/sdcard/accent.kdb"; + + public void testOpen() { + + /* + try { + TestData.GetDb(getContext(), ASSET, PASSWORD, KEYFILE, FILENAME); + } catch (Exception e) { + assertTrue("Failed to open database", false); + } + */ + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/OutputTests.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/OutputTests.java index bf93721fc..dde340c2b 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/OutputTests.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/OutputTests.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -26,10 +26,10 @@ import android.test.suitebuilder.TestSuiteBuilder; public class OutputTests extends TestSuite { - public static Test suite() { + public static Test suite() { - return new TestSuiteBuilder(AllTests.class) - .includePackages("com.kunzisoft.keepass.tests.output") - .build(); - } + return new TestSuiteBuilder(AllTests.class) + .includePackages("com.kunzisoft.keepass.tests.output") + .build(); + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java index dacde9f56..492c4e5f2 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -24,15 +24,15 @@ import junit.framework.TestCase; import com.kunzisoft.keepass.database.element.PwDate; public class PwDateTest extends TestCase { - public void testDate() { - PwDate jDate = new PwDate(System.currentTimeMillis()); - - PwDate intermediate = new PwDate(jDate); - - PwDate cDate = new PwDate(intermediate.getCDate(), 0); - - assertTrue("jDate and intermediate not equal", jDate.equals(intermediate)); - assertTrue("jDate and cDate not equal", cDate.equals(jDate)); - - } + public void testDate() { + PwDate jDate = new PwDate(System.currentTimeMillis()); + + PwDate intermediate = new PwDate(jDate); + + PwDate cDate = new PwDate(intermediate.getCDate(), 0); + + assertTrue("jDate and intermediate not equal", jDate.equals(intermediate)); + assertTrue("jDate and cDate not equal", cDate.equals(jDate)); + + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java index 8934da4de..8ec7691f4 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -31,33 +31,33 @@ import com.kunzisoft.keepass.database.element.PwEntryV3; import com.kunzisoft.keepass.tests.database.TestData; public class PwEntryTestV3 extends AndroidTestCase { - PwEntryV3 mPE; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - // mPE = (PwEntryV3) TestData.GetTest1(getContext()).getEntryAt(0); - - } - - public void testName() { - assertTrue("Name was " + mPE.getTitle(), mPE.getTitle().equals("Amazon")); - } - - public void testPassword() throws UnsupportedEncodingException { - String sPass = "12345"; - byte[] password = sPass.getBytes("UTF-8"); - - assertArrayEquals(password, mPE.getPasswordBytes()); - } - - public void testCreation() { - Calendar cal = Calendar.getInstance(); - cal.setTime(mPE.getCreationTime().getDate()); - - assertEquals("Incorrect year.", cal.get(Calendar.YEAR), 2009); - assertEquals("Incorrect month.", cal.get(Calendar.MONTH), 3); - assertEquals("Incorrect day.", cal.get(Calendar.DAY_OF_MONTH), 23); - } + PwEntryV3 mPE; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // mPE = (PwEntryV3) TestData.GetTest1(getContext()).getEntryAt(0); + + } + + public void testName() { + assertTrue("Name was " + mPE.getTitle(), mPE.getTitle().equals("Amazon")); + } + + public void testPassword() throws UnsupportedEncodingException { + String sPass = "12345"; + byte[] password = sPass.getBytes("UTF-8"); + + assertArrayEquals(password, mPE.getPasswordBytes()); + } + + public void testCreation() { + Calendar cal = Calendar.getInstance(); + cal.setTime(mPE.getCreationTime().getDate()); + + assertEquals("Incorrect year.", cal.get(Calendar.YEAR), 2009); + assertEquals("Incorrect month.", cal.get(Calendar.MONTH), 3); + assertEquals("Incorrect day.", cal.get(Calendar.DAY_OF_MONTH), 23); + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java index 1644ba56f..ab1b7737f 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.tests; import junit.framework.TestCase; public class PwEntryTestV4 extends TestCase { - public void testAssign() { + public void testAssign() { /* TODO Test PwEntryV4 entry = new PwEntryV4(); @@ -53,7 +53,7 @@ public class PwEntryTestV4 extends TestCase { /* This test is not so useful now that I am not implementing value equality for Entries assertTrue("Entries do not match.", entry.equals(target)); */ - - } + + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java index 692d12952..097b2f294 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -27,18 +27,18 @@ import com.kunzisoft.keepass.tests.database.TestData; public class PwGroupTest extends AndroidTestCase { - PwGroupV3 mPG; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - //mPG = (PwGroupV3) TestData.GetTest1(getContext()).getGroups().get(0); - - } - - public void testGroupName() { - //assertTrue("Name was " + mPG.getTitle(), mPG.getTitle().equals("Internet")); - } + PwGroupV3 mPG; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + //mPG = (PwGroupV3) TestData.GetTest1(getContext()).getGroups().get(0); + + } + + public void testGroupName() { + //assertTrue("Name was " + mPG.getTitle(), mPG.getTitle().equals("Internet")); + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java index bdec9c505..1a4ae6e1a 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -33,29 +33,29 @@ import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.UriUtil; public class TestUtil { - private static final File sdcard = Environment.getExternalStorageDirectory(); + private static final File sdcard = Environment.getExternalStorageDirectory(); - public static void extractKey(Context ctx, String asset, String target) throws Exception { - - InputStream key = ctx.getAssets().open(asset, AssetManager.ACCESS_STREAMING); - - FileOutputStream keyFile = new FileOutputStream(target); - while (true) { - byte[] buf = new byte[1024]; - int read = key.read(buf); - if ( read == -1 ) { - break; - } else { - keyFile.write(buf, 0, read); - } - } - - keyFile.close(); + public static void extractKey(Context ctx, String asset, String target) throws Exception { - } + InputStream key = ctx.getAssets().open(asset, AssetManager.ACCESS_STREAMING); - public static String getSdPath(String filename) { - File file = new File(sdcard, filename); - return file.getAbsolutePath(); - } + FileOutputStream keyFile = new FileOutputStream(target); + while (true) { + byte[] buf = new byte[1024]; + int read = key.read(buf); + if ( read == -1 ) { + break; + } else { + keyFile.write(buf, 0, read); + } + } + + keyFile.close(); + + } + + public static String getSdPath(String filename) { + File file = new File(sdcard, filename); + return file.getAbsolutePath(); + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java index d78d39932..aef346519 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -35,177 +35,177 @@ import com.kunzisoft.keepass.utils.Types; public class TypesTest extends TestCase { - public void testReadWriteLongZero() { - testReadWriteLong((byte) 0); - } - - public void testReadWriteLongMax() { - testReadWriteLong(Byte.MAX_VALUE); - } - - public void testReadWriteLongMin() { - testReadWriteLong(Byte.MIN_VALUE); - } - - public void testReadWriteLongRnd() { - Random rnd = new Random(); - byte[] buf = new byte[1]; - rnd.nextBytes(buf); - - testReadWriteLong(buf[0]); - } - - private void testReadWriteLong(byte value) { - byte[] orig = new byte[8]; - byte[] dest = new byte[8]; - - setArray(orig, value, 0, 8); - - long one = LEDataInputStream.readLong(orig, 0); - LEDataOutputStream.writeLong(one, dest, 0); - - assertArrayEquals(orig, dest); + public void testReadWriteLongZero() { + testReadWriteLong((byte) 0); + } - } - - public void testReadWriteIntZero() { - testReadWriteInt((byte) 0); - } - - public void testReadWriteIntMin() { - testReadWriteInt(Byte.MIN_VALUE); - } - - public void testReadWriteIntMax() { - testReadWriteInt(Byte.MAX_VALUE); - } - - private void testReadWriteInt(byte value) { - byte[] orig = new byte[4]; - byte[] dest = new byte[4]; - - for (int i = 0; i < 4; i++ ) { - orig[i] = 0; - } - - setArray(orig, value, 0, 4); - - int one = LEDataInputStream.readInt(orig, 0); - - LEDataOutputStream.writeInt(one, dest, 0); + public void testReadWriteLongMax() { + testReadWriteLong(Byte.MAX_VALUE); + } - assertArrayEquals(orig, dest); - - } - - private void setArray(byte[] buf, byte value, int offset, int size) { - for (int i = offset; i < offset + size; i++) { - buf[i] = value; - } - } - - public void testReadWriteShortOne() { - byte[] orig = new byte[2]; - byte[] dest = new byte[2]; - - orig[0] = 0; - orig[1] = 1; - - int one = LEDataInputStream.readUShort(orig, 0); - dest = LEDataOutputStream.writeUShortBuf(one); - - assertArrayEquals(orig, dest); - - } - - public void testReadWriteShortMin() { - testReadWriteShort(Byte.MIN_VALUE); - } - - public void testReadWriteShortMax() { - testReadWriteShort(Byte.MAX_VALUE); - } - - private void testReadWriteShort(byte value) { - byte[] orig = new byte[2]; - byte[] dest = new byte[2]; - - setArray(orig, value, 0, 2); - - int one = LEDataInputStream.readUShort(orig, 0); - LEDataOutputStream.writeUShort(one, dest, 0); - - assertArrayEquals(orig, dest); + public void testReadWriteLongMin() { + testReadWriteLong(Byte.MIN_VALUE); + } - } + public void testReadWriteLongRnd() { + Random rnd = new Random(); + byte[] buf = new byte[1]; + rnd.nextBytes(buf); - public void testReadWriteByteZero() { - testReadWriteByte((byte) 0); - } - - public void testReadWriteByteMin() { - testReadWriteByte(Byte.MIN_VALUE); - } - - public void testReadWriteByteMax() { - testReadWriteShort(Byte.MAX_VALUE); - } - - private void testReadWriteByte(byte value) { - byte[] orig = new byte[1]; - byte[] dest = new byte[1]; - - setArray(orig, value, 0, 1); - - int one = Types.readUByte(orig, 0); - Types.writeUByte(one, dest, 0); - - assertArrayEquals(orig, dest); - - } - - public void testDate() { - Calendar cal = Calendar.getInstance(); - - Calendar expected = Calendar.getInstance(); - expected.set(2008, 1, 2, 3, 4, 5); - - byte[] buf = PwDate.writeTime(expected.getTime(), cal); - Calendar actual = Calendar.getInstance(); - actual.setTime(PwDate.readTime(buf, 0, cal)); - - assertEquals("Year mismatch: ", 2008, actual.get(Calendar.YEAR)); - assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH)); - assertEquals("Day mismatch: ", 1, actual.get(Calendar.DAY_OF_MONTH)); - assertEquals("Hour mismatch: ", 3, actual.get(Calendar.HOUR_OF_DAY)); - assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE)); - assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND)); - } - - public void testUUID() { - Random rnd = new Random(); - byte[] bUUID = new byte[16]; - rnd.nextBytes(bUUID); - - UUID uuid = Types.bytestoUUID(bUUID); - byte[] eUUID = Types.UUIDtoBytes(uuid); - - assertArrayEquals("UUID match failed", bUUID, eUUID); - } + testReadWriteLong(buf[0]); + } - public void testULongMax() throws Exception { - byte[] ulongBytes = new byte[8]; - for (int i = 0; i < ulongBytes.length; i++) { - ulongBytes[i] = -1; - } + private void testReadWriteLong(byte value) { + byte[] orig = new byte[8]; + byte[] dest = new byte[8]; - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - LEDataOutputStream leos = new LEDataOutputStream(bos); - leos.writeLong(Types.ULONG_MAX_VALUE); - leos.close(); + setArray(orig, value, 0, 8); - byte[] uLongMax = bos.toByteArray(); + long one = LEDataInputStream.readLong(orig, 0); + LEDataOutputStream.writeLong(one, dest, 0); - assertArrayEquals(ulongBytes, uLongMax); - } + assertArrayEquals(orig, dest); + + } + + public void testReadWriteIntZero() { + testReadWriteInt((byte) 0); + } + + public void testReadWriteIntMin() { + testReadWriteInt(Byte.MIN_VALUE); + } + + public void testReadWriteIntMax() { + testReadWriteInt(Byte.MAX_VALUE); + } + + private void testReadWriteInt(byte value) { + byte[] orig = new byte[4]; + byte[] dest = new byte[4]; + + for (int i = 0; i < 4; i++ ) { + orig[i] = 0; + } + + setArray(orig, value, 0, 4); + + int one = LEDataInputStream.readInt(orig, 0); + + LEDataOutputStream.writeInt(one, dest, 0); + + assertArrayEquals(orig, dest); + + } + + private void setArray(byte[] buf, byte value, int offset, int size) { + for (int i = offset; i < offset + size; i++) { + buf[i] = value; + } + } + + public void testReadWriteShortOne() { + byte[] orig = new byte[2]; + byte[] dest = new byte[2]; + + orig[0] = 0; + orig[1] = 1; + + int one = LEDataInputStream.readUShort(orig, 0); + dest = LEDataOutputStream.writeUShortBuf(one); + + assertArrayEquals(orig, dest); + + } + + public void testReadWriteShortMin() { + testReadWriteShort(Byte.MIN_VALUE); + } + + public void testReadWriteShortMax() { + testReadWriteShort(Byte.MAX_VALUE); + } + + private void testReadWriteShort(byte value) { + byte[] orig = new byte[2]; + byte[] dest = new byte[2]; + + setArray(orig, value, 0, 2); + + int one = LEDataInputStream.readUShort(orig, 0); + LEDataOutputStream.writeUShort(one, dest, 0); + + assertArrayEquals(orig, dest); + + } + + public void testReadWriteByteZero() { + testReadWriteByte((byte) 0); + } + + public void testReadWriteByteMin() { + testReadWriteByte(Byte.MIN_VALUE); + } + + public void testReadWriteByteMax() { + testReadWriteShort(Byte.MAX_VALUE); + } + + private void testReadWriteByte(byte value) { + byte[] orig = new byte[1]; + byte[] dest = new byte[1]; + + setArray(orig, value, 0, 1); + + int one = Types.readUByte(orig, 0); + Types.writeUByte(one, dest, 0); + + assertArrayEquals(orig, dest); + + } + + public void testDate() { + Calendar cal = Calendar.getInstance(); + + Calendar expected = Calendar.getInstance(); + expected.set(2008, 1, 2, 3, 4, 5); + + byte[] buf = PwDate.writeTime(expected.getTime(), cal); + Calendar actual = Calendar.getInstance(); + actual.setTime(PwDate.readTime(buf, 0, cal)); + + assertEquals("Year mismatch: ", 2008, actual.get(Calendar.YEAR)); + assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH)); + assertEquals("Day mismatch: ", 1, actual.get(Calendar.DAY_OF_MONTH)); + assertEquals("Hour mismatch: ", 3, actual.get(Calendar.HOUR_OF_DAY)); + assertEquals("Minute mismatch: ", 4, actual.get(Calendar.MINUTE)); + assertEquals("Second mismatch: ", 5, actual.get(Calendar.SECOND)); + } + + public void testUUID() { + Random rnd = new Random(); + byte[] bUUID = new byte[16]; + rnd.nextBytes(bUUID); + + UUID uuid = Types.bytestoUUID(bUUID); + byte[] eUUID = Types.UUIDtoBytes(uuid); + + assertArrayEquals("UUID match failed", bUUID, eUUID); + } + + public void testULongMax() throws Exception { + byte[] ulongBytes = new byte[8]; + for (int i = 0; i < ulongBytes.length; i++) { + ulongBytes[i] = -1; + } + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + LEDataOutputStream leos = new LEDataOutputStream(bos); + leos.writeLong(Types.ULONG_MAX_VALUE); + leos.close(); + + byte[] uLongMax = bos.toByteArray(); + + assertArrayEquals(ulongBytes, uLongMax); + } } \ No newline at end of file diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/AESTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/AESTest.java index db147ee45..ab91076bc 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/AESTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/AESTest.java @@ -1,22 +1,22 @@ /* -* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. -* -* This file is part of KeePass DX. -* -* KeePass DX is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* KeePass DX is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with KeePass DX. If not, see . -* -*/ + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ package com.kunzisoft.keepass.tests.crypto; import com.kunzisoft.keepass.crypto.CipherFactory; @@ -38,46 +38,46 @@ import javax.crypto.spec.SecretKeySpec; import static org.junit.Assert.assertArrayEquals; public class AESTest extends TestCase { - - private Random mRand = new Random(); - - public void testEncrypt() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { - // Test above below and at the blocksize - testFinal(15); - testFinal(16); - testFinal(17); - - // Test random larger sizes - int size = mRand.nextInt(494) + 18; - testFinal(size); - } - - private void testFinal(int dataSize) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { - - // Generate some input - byte[] input = new byte[dataSize]; - mRand.nextBytes(input); - - // Generate key - byte[] keyArray = new byte[32]; - mRand.nextBytes(keyArray); - SecretKeySpec key = new SecretKeySpec(keyArray, "AES"); - - // Generate IV - byte[] ivArray = new byte[16]; - mRand.nextBytes(ivArray); - IvParameterSpec iv = new IvParameterSpec(ivArray); - - Cipher android = CipherFactory.getInstance("AES/CBC/PKCS5Padding", true); - android.init(Cipher.ENCRYPT_MODE, key, iv); - byte[] outAndroid = android.doFinal(input, 0, dataSize); - - Cipher nat = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); - nat.init(Cipher.ENCRYPT_MODE, key, iv); - byte[] outNative = nat.doFinal(input, 0, dataSize); - - assertArrayEquals("Arrays differ on size: " + dataSize, outAndroid, outNative); - } - - + + private Random mRand = new Random(); + + public void testEncrypt() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { + // Test above below and at the blocksize + testFinal(15); + testFinal(16); + testFinal(17); + + // Test random larger sizes + int size = mRand.nextInt(494) + 18; + testFinal(size); + } + + private void testFinal(int dataSize) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { + + // Generate some input + byte[] input = new byte[dataSize]; + mRand.nextBytes(input); + + // Generate key + byte[] keyArray = new byte[32]; + mRand.nextBytes(keyArray); + SecretKeySpec key = new SecretKeySpec(keyArray, "AES"); + + // Generate IV + byte[] ivArray = new byte[16]; + mRand.nextBytes(ivArray); + IvParameterSpec iv = new IvParameterSpec(ivArray); + + Cipher android = CipherFactory.getInstance("AES/CBC/PKCS5Padding", true); + android.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] outAndroid = android.doFinal(input, 0, dataSize); + + Cipher nat = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); + nat.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] outNative = nat.doFinal(input, 0, dataSize); + + assertArrayEquals("Arrays differ on size: " + dataSize, outAndroid, outNative); + } + + } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/CipherTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/CipherTest.java index 4adbdbcdc..2dd634a11 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/CipherTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/CipherTest.java @@ -1,22 +1,22 @@ /* -* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. -* -* This file is part of KeePass DX. -* -* KeePass DX is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* KeePass DX is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with KeePass DX. If not, see . -* -*/ + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ package com.kunzisoft.keepass.tests.crypto; import static org.junit.Assert.assertArrayEquals; @@ -44,57 +44,57 @@ import com.kunzisoft.keepass.stream.BetterCipherInputStream; import com.kunzisoft.keepass.stream.LEDataInputStream; public class CipherTest extends TestCase { - private Random rand = new Random(); - - public void testCipherFactory() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { - byte[] key = new byte[32]; - byte[] iv = new byte[16]; - - byte[] plaintext = new byte[1024]; - - rand.nextBytes(key); - rand.nextBytes(iv); - rand.nextBytes(plaintext); - - CipherEngine aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID); - Cipher encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv); - Cipher decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv); + private Random rand = new Random(); - byte[] secrettext = encrypt.doFinal(plaintext); - byte[] decrypttext = decrypt.doFinal(secrettext); - - assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext); - } + public void testCipherFactory() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + byte[] key = new byte[32]; + byte[] iv = new byte[16]; - public void testCipherStreams() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException { - final int MESSAGE_LENGTH = 1024; - - byte[] key = new byte[32]; - byte[] iv = new byte[16]; - - byte[] plaintext = new byte[MESSAGE_LENGTH]; - - rand.nextBytes(key); - rand.nextBytes(iv); - rand.nextBytes(plaintext); + byte[] plaintext = new byte[1024]; - CipherEngine aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID); - Cipher encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv); - Cipher decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv); + rand.nextBytes(key); + rand.nextBytes(iv); + rand.nextBytes(plaintext); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - CipherOutputStream cos = new CipherOutputStream(bos, encrypt); - cos.write(plaintext); - cos.close(); - - byte[] secrettext = bos.toByteArray(); - - ByteArrayInputStream bis = new ByteArrayInputStream(secrettext); - BetterCipherInputStream cis = new BetterCipherInputStream(bis, decrypt); - LEDataInputStream lis = new LEDataInputStream(cis); - - byte[] decrypttext = lis.readBytes(MESSAGE_LENGTH); - - assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext); - } + CipherEngine aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID); + Cipher encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv); + Cipher decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv); + + byte[] secrettext = encrypt.doFinal(plaintext); + byte[] decrypttext = decrypt.doFinal(secrettext); + + assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext); + } + + public void testCipherStreams() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException { + final int MESSAGE_LENGTH = 1024; + + byte[] key = new byte[32]; + byte[] iv = new byte[16]; + + byte[] plaintext = new byte[MESSAGE_LENGTH]; + + rand.nextBytes(key); + rand.nextBytes(iv); + rand.nextBytes(plaintext); + + CipherEngine aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID); + Cipher encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv); + Cipher decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + CipherOutputStream cos = new CipherOutputStream(bos, encrypt); + cos.write(plaintext); + cos.close(); + + byte[] secrettext = bos.toByteArray(); + + ByteArrayInputStream bis = new ByteArrayInputStream(secrettext); + BetterCipherInputStream cis = new BetterCipherInputStream(bis, decrypt); + LEDataInputStream lis = new LEDataInputStream(cis); + + byte[] decrypttext = lis.readBytes(MESSAGE_LENGTH); + + assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext); + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.java index ba9bc8da4..2c36c9bac 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.java @@ -1,22 +1,22 @@ /* -* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. -* -* This file is part of KeePass DX. -* -* KeePass DX is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* KeePass DX is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with KeePass DX. If not, see . -* -*/ + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ package com.kunzisoft.keepass.tests.crypto; import static org.junit.Assert.assertArrayEquals; @@ -30,37 +30,37 @@ import com.kunzisoft.keepass.crypto.finalkey.AndroidFinalKey; import com.kunzisoft.keepass.crypto.finalkey.NativeFinalKey; public class FinalKeyTest extends TestCase { - private Random mRand; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - mRand = new Random(); - } - - public void testNativeAndroid() throws IOException { - // Test both an old and an even number to test my flip variable - testNativeFinalKey(5); - testNativeFinalKey(6); - } - - private void testNativeFinalKey(int rounds) throws IOException { - byte[] seed = new byte[32]; - byte[] key = new byte[32]; - byte[] nativeKey; - byte[] androidKey; - - mRand.nextBytes(seed); - mRand.nextBytes(key); - - AndroidFinalKey aKey = new AndroidFinalKey(); - androidKey = aKey.transformMasterKey(seed, key, rounds); - - NativeFinalKey nKey = new NativeFinalKey(); - nativeKey = nKey.transformMasterKey(seed, key, rounds); - - assertArrayEquals("Does not match", androidKey, nativeKey); - - } + private Random mRand; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mRand = new Random(); + } + + public void testNativeAndroid() throws IOException { + // Test both an old and an even number to test my flip variable + testNativeFinalKey(5); + testNativeFinalKey(6); + } + + private void testNativeFinalKey(int rounds) throws IOException { + byte[] seed = new byte[32]; + byte[] key = new byte[32]; + byte[] nativeKey; + byte[] androidKey; + + mRand.nextBytes(seed); + mRand.nextBytes(key); + + AndroidFinalKey aKey = new AndroidFinalKey(); + androidKey = aKey.transformMasterKey(seed, key, rounds); + + NativeFinalKey nKey = new NativeFinalKey(); + nativeKey = nKey.transformMasterKey(seed, key, rounds); + + assertArrayEquals("Does not match", androidKey, nativeKey); + + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java index 15a46383c..ed01c2334 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -26,15 +26,15 @@ import com.kunzisoft.keepass.database.element.PwDatabaseV3; import com.kunzisoft.keepass.database.element.PwEntryV3; public class DeleteEntry extends AndroidTestCase { - private static final String GROUP1_NAME = "Group1"; - private static final String ENTRY1_NAME = "Test1"; - private static final String ENTRY2_NAME = "Test2"; - private static final String KEYFILE = ""; - private static final String PASSWORD = "12345"; - private static final String ASSET = "delete.kdb"; - private static final String FILENAME = "/sdcard/delete.kdb"; - - public void testDelete() { + private static final String GROUP1_NAME = "Group1"; + private static final String ENTRY1_NAME = "Test1"; + private static final String ENTRY2_NAME = "Test2"; + private static final String KEYFILE = ""; + private static final String PASSWORD = "12345"; + private static final String ASSET = "delete.kdb"; + private static final String FILENAME = "/sdcard/delete.kdb"; + + public void testDelete() { /* Database db; @@ -76,9 +76,9 @@ public class DeleteEntry extends AndroidTestCase { assertNull("Group 1 was not removed.", group1); */ - } - - private PwEntryV3 getEntry(PwDatabaseV3 pm, String name) { + } + + private PwEntryV3 getEntry(PwDatabaseV3 pm, String name) { /* TODO test List entries = pm.getEntries(); @@ -89,11 +89,11 @@ public class DeleteEntry extends AndroidTestCase { } } */ - return null; - - } - - private GroupVersioned getGroup(PwDatabase pm, String name) { + return null; + + } + + private GroupVersioned getGroup(PwDatabase pm, String name) { /* List groups = pm.getGroups(); for ( int i = 0; i < groups.size(); i++ ) { @@ -103,9 +103,9 @@ public class DeleteEntry extends AndroidTestCase { } } */ - - return null; - } - + + return null; + } + } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java index c8b8f7f0c..fa009c76f 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ import junit.framework.TestCase; public class EntryV4 extends TestCase { - public void testBackup() { + public void testBackup() { /* PwDatabaseV4 db = new PwDatabaseV4(); @@ -51,6 +51,6 @@ public class EntryV4 extends TestCase { assertEquals("Title2", backup.getTitle()); assertEquals("User2", backup.getUsername()); */ - } + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java index fc897cf0a..46121d9db 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -22,8 +22,8 @@ package com.kunzisoft.keepass.tests.database; import android.test.AndroidTestCase; public class Kdb3 extends AndroidTestCase { - - private void testKeyfile(String dbAsset, String keyAsset, String password) throws Exception { + + private void testKeyfile(String dbAsset, String keyAsset, String password) throws Exception { /* Context ctx = getContext(); @@ -40,14 +40,14 @@ public class Kdb3 extends AndroidTestCase { is.close(); */ - } - - public void testXMLKeyFile() throws Exception { - testKeyfile("kdb_with_xml_keyfile.kdb", "keyfile.key", "12345"); - } - - public void testBinary64KeyFile() throws Exception { - testKeyfile("binary-key.kdb", "binary.key", "12345"); - } + } + + public void testXMLKeyFile() throws Exception { + testKeyfile("kdb_with_xml_keyfile.kdb", "keyfile.key", "12345"); + } + + public void testBinary64KeyFile() throws Exception { + testKeyfile("binary-key.kdb", "binary.key", "12345"); + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java index 3f41d341f..6c1105909 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.tests.database; import android.test.AndroidTestCase; public class Kdb3Twofish extends AndroidTestCase { - public void testReadTwofish() throws Exception { + public void testReadTwofish() throws Exception { /* Context ctx = getContext(); @@ -37,5 +37,5 @@ public class Kdb3Twofish extends AndroidTestCase { is.close(); */ - } + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java index 6dc7c0dcf..99ca1a9d1 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -26,11 +26,11 @@ import android.test.AndroidTestCase; import java.io.InputStream; public class Kdb4Header extends AndroidTestCase { - public void testReadHeader() throws Exception { - Context ctx = getContext(); - - AssetManager am = ctx.getAssets(); - InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); + public void testReadHeader() throws Exception { + Context ctx = getContext(); + + AssetManager am = ctx.getAssets(); + InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); /* TODO Test @@ -45,5 +45,5 @@ public class Kdb4Header extends AndroidTestCase { is.close(); */ - } + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java index 738319a0a..8cf081d38 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -35,17 +35,17 @@ import java.util.UUID; import biz.source_code.base64Coder.Base64Coder; public class SprEngineTest extends AndroidTestCase { - private PwDatabaseV4 db; - private SprEngineV4 spr; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - Context ctx = getContext(); - - AssetManager am = ctx.getAssets(); - InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); + private PwDatabaseV4 db; + private SprEngineV4 spr; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + Context ctx = getContext(); + + AssetManager am = ctx.getAssets(); + InputStream is = am.open("test.kdbx", AssetManager.ACCESS_STREAMING); /* TODO Test @@ -56,12 +56,12 @@ public class SprEngineTest extends AndroidTestCase { spr = new SprEngineV4(); */ - } - - private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}"; - private final String ENCODE_UUID = "IN7RkON49Ui1UZ2ddqmLcw=="; - private final String RESULT = "Password"; - public void testRefReplace() { + } + + private final String REF = "{REF:P@I:2B1D56590D961F48A8CE8C392CE6CD35}"; + private final String ENCODE_UUID = "IN7RkON49Ui1UZ2ddqmLcw=="; + private final String RESULT = "Password"; + public void testRefReplace() { /* TODO TEST UUID entryUUID = decodeUUID(ENCODE_UUID); @@ -71,16 +71,16 @@ public class SprEngineTest extends AndroidTestCase { assertEquals(RESULT, spr.compile(REF, entry, db)); */ - - } - - private UUID decodeUUID(String encoded) { - if (encoded == null || encoded.length() == 0 ) { - return PwDatabase.UUID_ZERO; - } - - byte[] buf = Base64Coder.decode(encoded); - return Types.bytestoUUID(buf); - } + + } + + private UUID decodeUUID(String encoded) { + if (encoded == null || encoded.length() == 0 ) { + return PwDatabase.UUID_ZERO; + } + + byte[] buf = Base64Coder.decode(encoded); + return Types.bytestoUUID(buf); + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java index 98cc21f2d..0d4dee7e6 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -22,11 +22,11 @@ package com.kunzisoft.keepass.tests.database; import com.kunzisoft.keepass.database.element.Database; public class TestData { - private static final String TEST1_KEYFILE = ""; - private static final String TEST1_KDB = "test1.kdb"; - private static final String TEST1_PASSWORD = "12345"; + private static final String TEST1_KEYFILE = ""; + private static final String TEST1_KDB = "test1.kdb"; + private static final String TEST1_PASSWORD = "12345"; - private static Database mDb1; + private static Database mDb1; /* diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java index 55dccb177..68b221322 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java @@ -1,22 +1,22 @@ /* -* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. -* -* This file is part of KeePass DX. -* -* KeePass DX is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* KeePass DX is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with KeePass DX. If not, see . -* -*/ + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ package com.kunzisoft.keepass.tests.search; @@ -28,43 +28,43 @@ import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.element.GroupVersioned; public class SearchTest extends AndroidTestCase { - - private Database mDb; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - //mDb = TestData.GetDb1(getContext(), true); - } - - public void testSearch() { - GroupVersioned results = mDb.search("Amazon"); - //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); - - } - - public void testBackupIncluded() { - updateOmitSetting(false); - GroupVersioned results = mDb.search("BackupOnly"); - - //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); - } - - public void testBackupExcluded() { - updateOmitSetting(true); - GroupVersioned results = mDb.search("BackupOnly"); - - //assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0); - } - - private void updateOmitSetting(boolean setting) { - Context ctx = getContext(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - SharedPreferences.Editor editor = prefs.edit(); - - editor.putBoolean("settings_omitbackup_key", setting); - editor.commit(); - - } + + private Database mDb; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + //mDb = TestData.GetDb1(getContext(), true); + } + + public void testSearch() { + GroupVersioned results = mDb.search("Amazon"); + //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); + + } + + public void testBackupIncluded() { + updateOmitSetting(false); + GroupVersioned results = mDb.search("BackupOnly"); + + //assertTrue("Search result not found.", results.numbersOfChildEntries() > 0); + } + + public void testBackupExcluded() { + updateOmitSetting(true); + GroupVersioned results = mDb.search("BackupOnly"); + + //assertFalse("Search result found, but should not have been.", results.numbersOfChildEntries() > 0); + } + + private void updateOmitSetting(boolean setting) { + Context ctx = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); + SharedPreferences.Editor editor = prefs.edit(); + + editor.putBoolean("settings_omitbackup_key", setting); + editor.commit(); + + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/HashedBlock.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/HashedBlock.java index 6b6bc7f01..ed14b8552 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/HashedBlock.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/HashedBlock.java @@ -1,22 +1,22 @@ /* -* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. -* -* This file is part of KeePass DX. -* -* KeePass DX is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* KeePass DX is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with KeePass DX. If not, see . -* -*/ + * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ package com.kunzisoft.keepass.tests.stream; import static org.junit.Assert.assertArrayEquals; @@ -34,77 +34,77 @@ import com.kunzisoft.keepass.stream.HashedBlockInputStream; import com.kunzisoft.keepass.stream.HashedBlockOutputStream; public class HashedBlock extends TestCase { - - private static Random rand = new Random(); - public void testBlockAligned() throws IOException { - testSize(1024, 1024); - } - - public void testOffset() throws IOException { - testSize(1500, 1024); - } - - private void testSize(int blockSize, int bufferSize) throws IOException { - byte[] orig = new byte[blockSize]; - - rand.nextBytes(orig); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - HashedBlockOutputStream output = new HashedBlockOutputStream(bos, bufferSize); - output.write(orig); - output.close(); - - byte[] encoded = bos.toByteArray(); - - ByteArrayInputStream bis = new ByteArrayInputStream(encoded); - HashedBlockInputStream input = new HashedBlockInputStream(bis); + private static Random rand = new Random(); - ByteArrayOutputStream decoded = new ByteArrayOutputStream(); - while ( true ) { - byte[] buf = new byte[1024]; - int read = input.read(buf); - if ( read == -1 ) { - break; - } - - decoded.write(buf, 0, read); - } - - byte[] out = decoded.toByteArray(); - - assertArrayEquals(orig, out); - - } - - public void testGZIPStream() throws IOException { - final int testLength = 32000; - - byte[] orig = new byte[testLength]; - rand.nextBytes(orig); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - HashedBlockOutputStream hos = new HashedBlockOutputStream(bos); - GZIPOutputStream zos = new GZIPOutputStream(hos); - - zos.write(orig); - zos.close(); - - byte[] compressed = bos.toByteArray(); - ByteArrayInputStream bis = new ByteArrayInputStream(compressed); - HashedBlockInputStream his = new HashedBlockInputStream(bis); - GZIPInputStream zis = new GZIPInputStream(his); - - byte[] uncompressed = new byte[testLength]; - - int read = 0; - while (read != -1 && testLength - read > 0) { - read += zis.read(uncompressed, read, testLength - read); - - } - - assertArrayEquals("Output not equal to input", orig, uncompressed); - - - } + public void testBlockAligned() throws IOException { + testSize(1024, 1024); + } + + public void testOffset() throws IOException { + testSize(1500, 1024); + } + + private void testSize(int blockSize, int bufferSize) throws IOException { + byte[] orig = new byte[blockSize]; + + rand.nextBytes(orig); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + HashedBlockOutputStream output = new HashedBlockOutputStream(bos, bufferSize); + output.write(orig); + output.close(); + + byte[] encoded = bos.toByteArray(); + + ByteArrayInputStream bis = new ByteArrayInputStream(encoded); + HashedBlockInputStream input = new HashedBlockInputStream(bis); + + ByteArrayOutputStream decoded = new ByteArrayOutputStream(); + while ( true ) { + byte[] buf = new byte[1024]; + int read = input.read(buf); + if ( read == -1 ) { + break; + } + + decoded.write(buf, 0, read); + } + + byte[] out = decoded.toByteArray(); + + assertArrayEquals(orig, out); + + } + + public void testGZIPStream() throws IOException { + final int testLength = 32000; + + byte[] orig = new byte[testLength]; + rand.nextBytes(orig); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + HashedBlockOutputStream hos = new HashedBlockOutputStream(bos); + GZIPOutputStream zos = new GZIPOutputStream(hos); + + zos.write(orig); + zos.close(); + + byte[] compressed = bos.toByteArray(); + ByteArrayInputStream bis = new ByteArrayInputStream(compressed); + HashedBlockInputStream his = new HashedBlockInputStream(bis); + GZIPInputStream zis = new GZIPInputStream(his); + + byte[] uncompressed = new byte[testLength]; + + int read = 0; + while (read != -1 && testLength - read > 0) { + read += zis.read(uncompressed, read, testLength - read); + + } + + assertArrayEquals("Output not equal to input", orig, uncompressed); + + + } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.java index 3ee6130c6..12151eadd 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -30,28 +30,28 @@ public class StringUtilTest extends TestCase { private final String search = "BcDe"; private final String badSearch = "Ed"; - public void testIndexOfIgnoreCase1() { - assertEquals(1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH)); - } + public void testIndexOfIgnoreCase1() { + assertEquals(1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH)); + } - public void testIndexOfIgnoreCase2() { - assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH), 2); - } + public void testIndexOfIgnoreCase2() { + assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH), 2); + } - public void testIndexOfIgnoreCase3() { - assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH)); - } - - private final String repText = "AbCtestingaBc"; - private final String repSearch = "ABc"; - private final String repSearchBad = "CCCCCC"; - private final String repNew = "12345"; - private final String repResult = "12345testing12345"; - public void testReplaceAllIgnoresCase1() { - assertEquals(repResult, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH)); - } + public void testIndexOfIgnoreCase3() { + assertEquals(-1, StringUtil.INSTANCE.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH)); + } - public void testReplaceAllIgnoresCase2() { - assertEquals(repText, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH)); - } + private final String repText = "AbCtestingaBc"; + private final String repSearch = "ABc"; + private final String repSearchBad = "CCCCCC"; + private final String repNew = "12345"; + private final String repResult = "12345testing12345"; + public void testReplaceAllIgnoresCase1() { + assertEquals(repResult, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH)); + } + + public void testReplaceAllIgnoresCase2() { + assertEquals(repText, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH)); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.java b/app/src/main/java/com/kunzisoft/keepass/app/App.java index 2ccb90e21..2d888d74d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.java +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -29,46 +29,46 @@ import com.kunzisoft.keepass.stylish.Stylish; import java.util.Calendar; public class App extends MultiDexApplication { - private static Database db = null; - private static Calendar calendar = null; - private static RecentFileHistory fileHistory = null; - - public static Database getDB() { - if ( db == null ) { - db = new Database(); - } - return db; - } + private static Database db = null; + private static Calendar calendar = null; + private static RecentFileHistory fileHistory = null; - public static RecentFileHistory getFileHistory() { - return fileHistory; - } - - public static void setDB(Database d) { - db = d; - } + public static Database getDB() { + if ( db == null ) { + db = new Database(); + } + return db; + } - public static Calendar getCalendar() { - if ( calendar == null ) { - calendar = Calendar.getInstance(); - } - return calendar; - } + public static RecentFileHistory getFileHistory() { + return fileHistory; + } - @Override - public void onCreate() { - super.onCreate(); + public static void setDB(Database d) { + db = d; + } - Stylish.init(this); - fileHistory = new RecentFileHistory(this); - PRNGFixes.apply(); - } + public static Calendar getCalendar() { + if ( calendar == null ) { + calendar = Calendar.getInstance(); + } + return calendar; + } - @Override - public void onTerminate() { - if ( db != null ) { - db.closeAndClear(getApplicationContext()); - } - super.onTerminate(); - } + @Override + public void onCreate() { + super.onCreate(); + + Stylish.init(this); + fileHistory = new RecentFileHistory(this); + PRNGFixes.apply(); + } + + @Override + public void onTerminate() { + if ( db != null ) { + db.closeAndClear(getApplicationContext()); + } + super.onTerminate(); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.java b/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.java index 156936444..a770898f7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.java +++ b/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -25,15 +25,15 @@ import android.app.backup.SharedPreferencesBackupHelper; @SuppressLint("NewApi") public class SettingsBackupAgent extends BackupAgentHelper { - - private static final String PREFS_BACKUP_KEY = "prefs"; - - @Override - public void onCreate() { - String defaultPrefs = this.getPackageName() + "_preferences"; - - SharedPreferencesBackupHelper prefHelper = new SharedPreferencesBackupHelper(this, defaultPrefs); - addHelper(PREFS_BACKUP_KEY, prefHelper); - } + + private static final String PREFS_BACKUP_KEY = "prefs"; + + @Override + public void onCreate() { + String defaultPrefs = this.getPackageName() + "_preferences"; + + SharedPreferencesBackupHelper prefHelper = new SharedPreferencesBackupHelper(this, defaultPrefs); + addHelper(PREFS_BACKUP_KEY, prefHelper); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.java b/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.java index 97b1af712..4ef047276 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/AESProvider.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -23,14 +23,14 @@ import java.security.Provider; public final class AESProvider extends Provider { - /** - * - */ - private static final long serialVersionUID = -3846349284296062658L; + /** + * + */ + private static final long serialVersionUID = -3846349284296062658L; - public AESProvider() { - super("AESProvider", 1.0, ""); - put("Cipher.AES",NativeAESCipherSpi.class.getName()); - } + public AESProvider() { + super("AESProvider", 1.0, ""); + put("Cipher.AES",NativeAESCipherSpi.class.getName()); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java b/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java index 40e82d867..8abdcbac8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/CipherFactory.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -38,58 +38,58 @@ import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; public class CipherFactory { - private static boolean blacklistInit = false; - private static boolean blacklisted; + private static boolean blacklistInit = false; + private static boolean blacklisted; - static { - Security.addProvider(new BouncyCastleProvider()); - } - - public static Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { - return getInstance(transformation, false); - } - - public static Cipher getInstance(String transformation, boolean androidOverride) throws NoSuchAlgorithmException, NoSuchPaddingException { - // Return the native AES if it is possible - if ( (!deviceBlacklisted()) && (!androidOverride) && hasNativeImplementation(transformation) && NativeLib.loaded() ) { - return Cipher.getInstance(transformation, new AESProvider()); - } else { + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { + return getInstance(transformation, false); + } + + public static Cipher getInstance(String transformation, boolean androidOverride) throws NoSuchAlgorithmException, NoSuchPaddingException { + // Return the native AES if it is possible + if ( (!deviceBlacklisted()) && (!androidOverride) && hasNativeImplementation(transformation) && NativeLib.loaded() ) { + return Cipher.getInstance(transformation, new AESProvider()); + } else { return Cipher.getInstance(transformation); - } - } - - public static boolean deviceBlacklisted() { - if (!blacklistInit) { - blacklistInit = true; - - // The Acer Iconia A500 is special and seems to always crash in the native crypto libraries - blacklisted = Build.MODEL.equals("A500"); - } - return blacklisted; - } - - private static boolean hasNativeImplementation(String transformation) { - return transformation.equals("AES/CBC/PKCS5Padding"); - } + } + } + + public static boolean deviceBlacklisted() { + if (!blacklistInit) { + blacklistInit = true; + + // The Acer Iconia A500 is special and seems to always crash in the native crypto libraries + blacklisted = Build.MODEL.equals("A500"); + } + return blacklisted; + } + + private static boolean hasNativeImplementation(String transformation) { + return transformation.equals("AES/CBC/PKCS5Padding"); + } - /** Generate appropriate cipher based on KeePass 2.x UUID's - * @param uuid - * @return - * @throws NoSuchPaddingException - * @throws NoSuchAlgorithmException - * @throws InvalidAlgorithmParameterException - * @throws InvalidKeyException - */ - public static CipherEngine getInstance(UUID uuid) throws NoSuchAlgorithmException { - if ( uuid.equals(AesEngine.CIPHER_UUID) ) { - return new AesEngine(); - } else if ( uuid.equals(TwofishEngine.CIPHER_UUID) ) { - return new TwofishEngine(); - } else if ( uuid.equals(ChaCha20Engine.CIPHER_UUID)) { - return new ChaCha20Engine(); - } - - throw new NoSuchAlgorithmException("UUID unrecognized."); - } + /** Generate appropriate cipher based on KeePass 2.x UUID's + * @param uuid + * @return + * @throws NoSuchPaddingException + * @throws NoSuchAlgorithmException + * @throws InvalidAlgorithmParameterException + * @throws InvalidKeyException + */ + public static CipherEngine getInstance(UUID uuid) throws NoSuchAlgorithmException { + if ( uuid.equals(AesEngine.CIPHER_UUID) ) { + return new AesEngine(); + } else if ( uuid.equals(TwofishEngine.CIPHER_UUID) ) { + return new TwofishEngine(); + } else if ( uuid.equals(ChaCha20Engine.CIPHER_UUID)) { + return new ChaCha20Engine(); + } + + throw new NoSuchAlgorithmException("UUID unrecognized."); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java index e24550706..15d09bdc4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeAESCipherSpi.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -44,284 +44,284 @@ import javax.crypto.spec.IvParameterSpec; public class NativeAESCipherSpi extends CipherSpi { - private static final String TAG = NativeAESCipherSpi.class.getName(); + private static final String TAG = NativeAESCipherSpi.class.getName(); - private static boolean mIsStaticInit = false; - private static HashMap, Long> mCleanup = new HashMap, Long>(); - private static ReferenceQueue mQueue = new ReferenceQueue(); - - private final int AES_BLOCK_SIZE = 16; - private byte[] mIV; - - private boolean mIsInited = false; - private boolean mEncrypting = false; - private long mCtxPtr; - - private boolean mPadding = false; - - private static void staticInit() { - mIsStaticInit = true; - - // Start the cipher context cleanup thread to run forever - (new Thread(new Cleanup())).start(); - } - - private static void addToCleanupQueue(NativeAESCipherSpi ref, long ptr) { - Log.d(TAG, "queued cipher context: " + ptr); - mCleanup.put(new PhantomReference(ref, mQueue), ptr); - } - - /** Work with the garbage collector to clean up openssl memory when the cipher - * context is garbage collected. - * @author bpellin - * - */ - private static class Cleanup implements Runnable { + private static boolean mIsStaticInit = false; + private static HashMap, Long> mCleanup = new HashMap, Long>(); + private static ReferenceQueue mQueue = new ReferenceQueue(); - public void run() { - while (true) { - try { - Reference ref = mQueue.remove(); - - long ctx = mCleanup.remove(ref); - nCleanup(ctx); - Log.d(TAG, "Cleaned up cipher context: " + ctx); - - } catch (InterruptedException e) { - // Do nothing, but resume looping if mQueue.remove is interrupted - } - } - } - - } - - private static native void nCleanup(long ctxPtr); + private final int AES_BLOCK_SIZE = 16; + private byte[] mIV; - public NativeAESCipherSpi() { - if ( ! mIsStaticInit ) { - staticInit(); - } - } - - @Override - protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) - throws IllegalBlockSizeException, BadPaddingException { - int maxSize = engineGetOutputSize(inputLen); - byte[] output = new byte[maxSize]; - - int finalSize; - - try { - finalSize = doFinal(input, inputOffset, inputLen, output, 0); - } catch (ShortBufferException e) { - // This shouldn't be possible rethrow as RuntimeException - throw new RuntimeException("Short buffer exception shouldn't be possible from here."); - } - - if ( maxSize == finalSize ) { - return output; - } else { - // TODO: Special doFinal to avoid this copy - byte[] exact = new byte[finalSize]; - System.arraycopy(output, 0, exact, 0, finalSize); - return exact; - } - } + private boolean mIsInited = false; + private boolean mEncrypting = false; + private long mCtxPtr; - @Override - protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, - byte[] output, int outputOffset) throws ShortBufferException, - IllegalBlockSizeException, BadPaddingException { - - int result = doFinal(input, inputOffset, inputLen, output, outputOffset); - - if ( result == -1 ) { - throw new ShortBufferException(); - } - - return result; - } - - private int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) - throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { - - int outputSize = engineGetOutputSize(inputLen); - - int updateAmt; - if (input != null && inputLen > 0) { - updateAmt = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); - } else { - updateAmt = 0; - } - - int finalAmt = nFinal(mCtxPtr, mPadding, output, outputOffset + updateAmt, outputSize - updateAmt); - - int out = updateAmt + finalAmt; - - - return out; - } - - private native int nFinal(long ctxPtr, boolean usePadding, byte[] output, int outputOffest, int outputSize) - throws ShortBufferException, IllegalBlockSizeException, BadPaddingException; + private boolean mPadding = false; - @Override - protected int engineGetBlockSize() { - return AES_BLOCK_SIZE; - } + private static void staticInit() { + mIsStaticInit = true; - @Override - protected byte[] engineGetIV() { - byte[] copyIV = new byte[0]; - if (mIV != null) { - int lengthIV = mIV.length; - copyIV = new byte[lengthIV]; - System.arraycopy(mIV, 0, copyIV, 0, lengthIV); - } - return copyIV; - } + // Start the cipher context cleanup thread to run forever + (new Thread(new Cleanup())).start(); + } - @Override - protected int engineGetOutputSize(int inputLen) { - return inputLen + nGetCacheSize(mCtxPtr) + AES_BLOCK_SIZE; - } - - private native int nGetCacheSize(long ctxPtr); + private static void addToCleanupQueue(NativeAESCipherSpi ref, long ptr) { + Log.d(TAG, "queued cipher context: " + ptr); + mCleanup.put(new PhantomReference(ref, mQueue), ptr); + } - @Override - protected AlgorithmParameters engineGetParameters() { - // TODO Auto-generated method stub - return null; - } + /** Work with the garbage collector to clean up openssl memory when the cipher + * context is garbage collected. + * @author bpellin + * + */ + private static class Cleanup implements Runnable { - @Override - protected void engineInit(int opmode, Key key, SecureRandom random) - throws InvalidKeyException { + public void run() { + while (true) { + try { + Reference ref = mQueue.remove(); - byte[] ivArray = new byte[16]; - random.nextBytes(ivArray); - - init(opmode, key, new IvParameterSpec(ivArray)); - } + long ctx = mCleanup.remove(ref); + nCleanup(ctx); + Log.d(TAG, "Cleaned up cipher context: " + ctx); - @Override - protected void engineInit(int opmode, Key key, - AlgorithmParameterSpec params, SecureRandom random) - throws InvalidKeyException, InvalidAlgorithmParameterException { - - IvParameterSpec ivparam; - - if ( params instanceof IvParameterSpec ) { - ivparam = (IvParameterSpec) params; - } else { - throw new InvalidAlgorithmParameterException("params must be an IvParameterSpec."); - } - - init(opmode, key, ivparam); - } - + } catch (InterruptedException e) { + // Do nothing, but resume looping if mQueue.remove is interrupted + } + } + } - @Override - protected void engineInit(int opmode, Key key, AlgorithmParameters params, - SecureRandom random) throws InvalidKeyException, - InvalidAlgorithmParameterException { - - try { - engineInit(opmode, key, params.getParameterSpec(AlgorithmParameterSpec.class), random); - } catch (InvalidParameterSpecException e) { - throw new InvalidAlgorithmParameterException(e); - } + } - } + private static native void nCleanup(long ctxPtr); - private void init(int opmode, Key key, IvParameterSpec params) { - if ( mIsInited ) { - // Do not allow multiple inits - assert(true); - throw new RuntimeException("Don't allow multiple inits"); - } else { - NativeLib.init(); - mIsInited = true; - } - - mIV = params.getIV(); - mEncrypting = opmode == Cipher.ENCRYPT_MODE; - mCtxPtr = nInit(mEncrypting, key.getEncoded(), mIV); - addToCleanupQueue(this, mCtxPtr); - } - - private native long nInit(boolean encrypting, byte[] key, byte[] iv); - - @Override - protected void engineSetMode(String mode) throws NoSuchAlgorithmException { - if ( ! mode.equals("CBC") ) { - throw new NoSuchAlgorithmException("This only supports CBC mode"); - } - } + public NativeAESCipherSpi() { + if ( ! mIsStaticInit ) { + staticInit(); + } + } - @Override - protected void engineSetPadding(String padding) - throws NoSuchPaddingException { - - if ( ! mIsInited ) { - NativeLib.init(); - } - - if ( padding.length() == 0 ) { - return; - } + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + int maxSize = engineGetOutputSize(inputLen); + byte[] output = new byte[maxSize]; - if ( ! padding.equals("PKCS5Padding") ) { - throw new NoSuchPaddingException("Only supports PKCS5Padding."); - } - - mPadding = true; - - } - - @Override - protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { - int maxSize = engineGetOutputSize(inputLen); - byte output[] = new byte[maxSize]; - - int updateSize = update(input, inputOffset, inputLen, output, 0); - - if ( updateSize == maxSize ) { - return output; - } else { - // TODO: We could optimize update for this case to avoid this extra copy - byte[] exact = new byte[updateSize]; - System.arraycopy(output, 0, exact, 0, updateSize); - return exact; - } - - } + int finalSize; + + try { + finalSize = doFinal(input, inputOffset, inputLen, output, 0); + } catch (ShortBufferException e) { + // This shouldn't be possible rethrow as RuntimeException + throw new RuntimeException("Short buffer exception shouldn't be possible from here."); + } + + if ( maxSize == finalSize ) { + return output; + } else { + // TODO: Special doFinal to avoid this copy + byte[] exact = new byte[finalSize]; + System.arraycopy(output, 0, exact, 0, finalSize); + return exact; + } + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException { + + int result = doFinal(input, inputOffset, inputLen, output, outputOffset); + + if ( result == -1 ) { + throw new ShortBufferException(); + } + + return result; + } + + private int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + + int outputSize = engineGetOutputSize(inputLen); + + int updateAmt; + if (input != null && inputLen > 0) { + updateAmt = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); + } else { + updateAmt = 0; + } + + int finalAmt = nFinal(mCtxPtr, mPadding, output, outputOffset + updateAmt, outputSize - updateAmt); + + int out = updateAmt + finalAmt; + + + return out; + } + + private native int nFinal(long ctxPtr, boolean usePadding, byte[] output, int outputOffest, int outputSize) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException; + + @Override + protected int engineGetBlockSize() { + return AES_BLOCK_SIZE; + } + + @Override + protected byte[] engineGetIV() { + byte[] copyIV = new byte[0]; + if (mIV != null) { + int lengthIV = mIV.length; + copyIV = new byte[lengthIV]; + System.arraycopy(mIV, 0, copyIV, 0, lengthIV); + } + return copyIV; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return inputLen + nGetCacheSize(mCtxPtr) + AES_BLOCK_SIZE; + } + + private native int nGetCacheSize(long ctxPtr); + + @Override + protected AlgorithmParameters engineGetParameters() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + + byte[] ivArray = new byte[16]; + random.nextBytes(ivArray); + + init(opmode, key, new IvParameterSpec(ivArray)); + } + + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + IvParameterSpec ivparam; + + if ( params instanceof IvParameterSpec ) { + ivparam = (IvParameterSpec) params; + } else { + throw new InvalidAlgorithmParameterException("params must be an IvParameterSpec."); + } + + init(opmode, key, ivparam); + } + + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + + try { + engineInit(opmode, key, params.getParameterSpec(AlgorithmParameterSpec.class), random); + } catch (InvalidParameterSpecException e) { + throw new InvalidAlgorithmParameterException(e); + } + + } + + private void init(int opmode, Key key, IvParameterSpec params) { + if ( mIsInited ) { + // Do not allow multiple inits + assert(true); + throw new RuntimeException("Don't allow multiple inits"); + } else { + NativeLib.init(); + mIsInited = true; + } + + mIV = params.getIV(); + mEncrypting = opmode == Cipher.ENCRYPT_MODE; + mCtxPtr = nInit(mEncrypting, key.getEncoded(), mIV); + addToCleanupQueue(this, mCtxPtr); + } + + private native long nInit(boolean encrypting, byte[] key, byte[] iv); + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + if ( ! mode.equals("CBC") ) { + throw new NoSuchAlgorithmException("This only supports CBC mode"); + } + } + + @Override + protected void engineSetPadding(String padding) + throws NoSuchPaddingException { + + if ( ! mIsInited ) { + NativeLib.init(); + } + + if ( padding.length() == 0 ) { + return; + } + + if ( ! padding.equals("PKCS5Padding") ) { + throw new NoSuchPaddingException("Only supports PKCS5Padding."); + } + + mPadding = true; + + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + int maxSize = engineGetOutputSize(inputLen); + byte output[] = new byte[maxSize]; + + int updateSize = update(input, inputOffset, inputLen, output, 0); + + if ( updateSize == maxSize ) { + return output; + } else { + // TODO: We could optimize update for this case to avoid this extra copy + byte[] exact = new byte[updateSize]; + System.arraycopy(output, 0, exact, 0, updateSize); + return exact; + } + + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) throws ShortBufferException { + + int result = update(input, inputOffset, inputLen, output, outputOffset); + + if ( result == -1 ) { + throw new ShortBufferException("Insufficient buffer."); + } + + return result; + + } + + int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + int outputSize = engineGetOutputSize(inputLen); + + int out = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); + + + return out; + + + } + + private native int nUpdate(long ctxPtr, byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset, int outputSize); - @Override - protected int engineUpdate(byte[] input, int inputOffset, int inputLen, - byte[] output, int outputOffset) throws ShortBufferException { - - int result = update(input, inputOffset, inputLen, output, outputOffset); - - if ( result == -1 ) { - throw new ShortBufferException("Insufficient buffer."); - } - - return result; - - } - - int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { - int outputSize = engineGetOutputSize(inputLen); - - int out = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); - - - return out; - - - } - - private native int nUpdate(long ctxPtr, byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset, int outputSize); - } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.java b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.java index 3f1b3f5ea..88a774220 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/NativeLib.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -20,27 +20,27 @@ package com.kunzisoft.keepass.crypto; public class NativeLib { - private static boolean isLoaded = false; - private static boolean loadSuccess = false; - - public static boolean loaded() { - return init(); - } - - public static boolean init() { - if ( ! isLoaded ) { - try { - System.loadLibrary("final-key"); - System.loadLibrary("argon2"); - } catch ( UnsatisfiedLinkError e) { - return false; - } - isLoaded = true; - loadSuccess = true; - } - - return loadSuccess; - - } + private static boolean isLoaded = false; + private static boolean loadSuccess = false; + + public static boolean loaded() { + return init(); + } + + public static boolean init() { + if ( ! isLoaded ) { + try { + System.loadLibrary("final-key"); + System.loadLibrary("argon2"); + } catch ( UnsatisfiedLinkError e) { + return false; + } + isLoaded = true; + loadSuccess = true; + } + + return loadSuccess; + + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java b/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java index d4a8f9866..2d5643dd1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/PwStreamCipherFactory.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -26,48 +26,48 @@ import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.ParametersWithIV; public class PwStreamCipherFactory { - public static StreamCipher getInstance(CrsAlgorithm alg, byte[] key) { - if ( alg == CrsAlgorithm.Salsa20 ) { - return getSalsa20(key); - } else if (alg == CrsAlgorithm.ChaCha20) { - return getChaCha20(key); - } else { - return null; - } - } - - - private static final byte[] SALSA_IV = new byte[]{ (byte)0xE8, 0x30, 0x09, 0x4B, + public static StreamCipher getInstance(CrsAlgorithm alg, byte[] key) { + if ( alg == CrsAlgorithm.Salsa20 ) { + return getSalsa20(key); + } else if (alg == CrsAlgorithm.ChaCha20) { + return getChaCha20(key); + } else { + return null; + } + } + + + private static final byte[] SALSA_IV = new byte[]{ (byte)0xE8, 0x30, 0x09, 0x4B, (byte)0x97, 0x20, 0x5D, 0x2A }; - private static StreamCipher getSalsa20(byte[] key) { - // Build stream cipher key - byte[] key32 = CryptoUtil.hashSha256(key); + private static StreamCipher getSalsa20(byte[] key) { + // Build stream cipher key + byte[] key32 = CryptoUtil.hashSha256(key); - KeyParameter keyParam = new KeyParameter(key32); - ParametersWithIV ivParam = new ParametersWithIV(keyParam, SALSA_IV); + KeyParameter keyParam = new KeyParameter(key32); + ParametersWithIV ivParam = new ParametersWithIV(keyParam, SALSA_IV); - StreamCipher cipher = new Salsa20Engine(); - cipher.init(true, ivParam); + StreamCipher cipher = new Salsa20Engine(); + cipher.init(true, ivParam); - return cipher; - } + return cipher; + } - private static StreamCipher getChaCha20(byte[] key) { - // Build stream cipher key - byte[] hash = CryptoUtil.hashSha512(key); - byte[] key32 = new byte[32]; - byte[] iv = new byte[12]; + private static StreamCipher getChaCha20(byte[] key) { + // Build stream cipher key + byte[] hash = CryptoUtil.hashSha512(key); + byte[] key32 = new byte[32]; + byte[] iv = new byte[12]; - System.arraycopy(hash, 0, key32, 0, 32); + System.arraycopy(hash, 0, key32, 0, 32); System.arraycopy(hash, 32, iv, 0, 12); - KeyParameter keyParam = new KeyParameter(key32); - ParametersWithIV ivParam = new ParametersWithIV(keyParam, iv); + KeyParameter keyParam = new KeyParameter(key32); + ParametersWithIV ivParam = new ParametersWithIV(keyParam, iv); StreamCipher cipher = new ChaCha7539Engine(); - cipher.init(true, ivParam); - - return cipher; - } + cipher.init(true, ivParam); + + return cipher; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidFinalKey.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidFinalKey.java index 27b536379..b573f8629 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidFinalKey.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/AndroidFinalKey.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -31,48 +31,48 @@ import javax.crypto.spec.SecretKeySpec; public class AndroidFinalKey extends FinalKey { - @Override - public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, long rounds) throws IOException { - Cipher cipher; - try { - cipher = Cipher.getInstance("AES/ECB/NoPadding"); - } catch (NoSuchAlgorithmException e) { - throw new IOException("NoSuchAlgorithm: " + e.getMessage()); - } catch (NoSuchPaddingException e) { - throw new IOException("NoSuchPadding: " + e.getMessage()); - } + @Override + public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, long rounds) throws IOException { + Cipher cipher; + try { + cipher = Cipher.getInstance("AES/ECB/NoPadding"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("NoSuchAlgorithm: " + e.getMessage()); + } catch (NoSuchPaddingException e) { + throw new IOException("NoSuchPadding: " + e.getMessage()); + } - try { - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(pKeySeed, "AES")); - } catch (InvalidKeyException e) { - throw new IOException("InvalidPasswordException: " + e.getMessage()); - } + try { + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(pKeySeed, "AES")); + } catch (InvalidKeyException e) { + throw new IOException("InvalidPasswordException: " + e.getMessage()); + } - // Encrypt key rounds times - byte[] newKey = new byte[pKey.length]; - System.arraycopy(pKey, 0, newKey, 0, pKey.length); - byte[] destKey = new byte[pKey.length]; - for (int i = 0; i < rounds; i++) { - try { - cipher.update(newKey, 0, newKey.length, destKey, 0); - System.arraycopy(destKey, 0, newKey, 0, newKey.length); + // Encrypt key rounds times + byte[] newKey = new byte[pKey.length]; + System.arraycopy(pKey, 0, newKey, 0, pKey.length); + byte[] destKey = new byte[pKey.length]; + for (int i = 0; i < rounds; i++) { + try { + cipher.update(newKey, 0, newKey.length, destKey, 0); + System.arraycopy(destKey, 0, newKey, 0, newKey.length); - } catch (ShortBufferException e) { - throw new IOException("Short buffer: " + e.getMessage()); - } - } + } catch (ShortBufferException e) { + throw new IOException("Short buffer: " + e.getMessage()); + } + } - // Hash the key - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - assert true; - throw new IOException("SHA-256 not implemented here: " + e.getMessage()); - } + // Hash the key + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + assert true; + throw new IOException("SHA-256 not implemented here: " + e.getMessage()); + } - md.update(newKey); - return md.digest(); - } + md.update(newKey); + return md.digest(); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKey.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKey.java index cded04667..9495a6ce4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKey.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKey.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -22,5 +22,5 @@ package com.kunzisoft.keepass.crypto.finalkey; import java.io.IOException; public abstract class FinalKey { - public abstract byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException; + public abstract byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException; } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKeyFactory.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKeyFactory.java index 429dd21e1..851e29a52 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKeyFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/FinalKeyFactory.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -22,17 +22,17 @@ package com.kunzisoft.keepass.crypto.finalkey; import com.kunzisoft.keepass.crypto.CipherFactory; public class FinalKeyFactory { - public static FinalKey createFinalKey() { - return createFinalKey(false); - } - - public static FinalKey createFinalKey(boolean androidOverride) { - // Prefer the native final key implementation - if ( !CipherFactory.deviceBlacklisted() && !androidOverride && NativeFinalKey.availble() ) { - return new NativeFinalKey(); - } else { - // Fall back on the android crypto implementation - return new AndroidFinalKey(); - } - } + public static FinalKey createFinalKey() { + return createFinalKey(false); + } + + public static FinalKey createFinalKey(boolean androidOverride) { + // Prefer the native final key implementation + if ( !CipherFactory.deviceBlacklisted() && !androidOverride && NativeFinalKey.availble() ) { + return new NativeFinalKey(); + } else { + // Fall back on the android crypto implementation + return new AndroidFinalKey(); + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java index 5e0900d92..66fae82b7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java +++ b/app/src/main/java/com/kunzisoft/keepass/crypto/finalkey/NativeFinalKey.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -25,22 +25,22 @@ import java.io.IOException; public class NativeFinalKey extends FinalKey { - - public static boolean availble() { - return NativeLib.init(); - } - @Override - public byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException { - NativeLib.init(); - - return nTransformMasterKey(seed, key, rounds); + public static boolean availble() { + return NativeLib.init(); + } - } - - private static native byte[] nTransformMasterKey(byte[] seed, byte[] key, long rounds); + @Override + public byte[] transformMasterKey(byte[] seed, byte[] key, long rounds) throws IOException { + NativeLib.init(); - // For testing + return nTransformMasterKey(seed, key, rounds); + + } + + private static native byte[] nTransformMasterKey(byte[] seed, byte[] key, long rounds); + + // For testing /* public static byte[] reflect(byte[] key) { NativeLib.init(); @@ -50,6 +50,6 @@ public class NativeFinalKey extends FinalKey { private static native byte[] nativeReflect(byte[] key); */ - + } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java index eef5ea7a9..952a041c7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.java @@ -1,6 +1,6 @@ /* * Copyright 2019 Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -39,126 +39,126 @@ import java.util.*; */ public class PwDatabaseV3 extends PwDatabase { - private static final int DEFAULT_ENCRYPTION_ROUNDS = 300; + private static final int DEFAULT_ENCRYPTION_ROUNDS = 300; - private int numKeyEncRounds; + private int numKeyEncRounds; - protected PwGroupV3 rootGroup; + protected PwGroupV3 rootGroup; public PwDatabaseV3() { algorithm = PwEncryptionAlgorithm.AESRijndael; numKeyEncRounds = DEFAULT_ENCRYPTION_ROUNDS; } - @Override - public String getVersion() { - return "KeePass 1"; - } + @Override + public String getVersion() { + return "KeePass 1"; + } - @Override - public List getAvailableEncryptionAlgorithms() { - List list = new ArrayList<>(); - list.add(PwEncryptionAlgorithm.AESRijndael); - return list; - } + @Override + public List getAvailableEncryptionAlgorithms() { + List list = new ArrayList<>(); + list.add(PwEncryptionAlgorithm.AESRijndael); + return list; + } - public List getRootGroups() { + public List getRootGroups() { List kids = new ArrayList<>(); - for (Map.Entry group : groupIndexes.entrySet()) { - if (group.getValue().getLevel() == 0) - kids.add(group.getValue()); - } - return kids; - } + for (Map.Entry group : groupIndexes.entrySet()) { + if (group.getValue().getLevel() == 0) + kids.add(group.getValue()); + } + return kids; + } - private void assignGroupsChildren(PwGroupV3 parent) { - int levelToCheck = parent.getLevel() + 1; - boolean startFromParentPosition = false; - for (PwGroupV3 groupToCheck: getGroupIndexes()) { - if (getRootGroup().getNodeId().equals(parent.getNodeId()) - || groupToCheck.getNodeId().equals(parent.getNodeId())) { - startFromParentPosition = true; - } - if (startFromParentPosition) { - if (groupToCheck.getLevel() < levelToCheck) - break; - else if (groupToCheck.getLevel() == levelToCheck) - parent.addChildGroup(groupToCheck); - } - } - } + private void assignGroupsChildren(PwGroupV3 parent) { + int levelToCheck = parent.getLevel() + 1; + boolean startFromParentPosition = false; + for (PwGroupV3 groupToCheck: getGroupIndexes()) { + if (getRootGroup().getNodeId().equals(parent.getNodeId()) + || groupToCheck.getNodeId().equals(parent.getNodeId())) { + startFromParentPosition = true; + } + if (startFromParentPosition) { + if (groupToCheck.getLevel() < levelToCheck) + break; + else if (groupToCheck.getLevel() == levelToCheck) + parent.addChildGroup(groupToCheck); + } + } + } - private void assignEntriesChildren(PwGroupV3 parent) { - for (PwEntryV3 entry : getEntryIndexes()) { - if (entry.getParent().getNodeId().equals(parent.getNodeId())) - parent.addChildEntry(entry); - } - } + private void assignEntriesChildren(PwGroupV3 parent) { + for (PwEntryV3 entry : getEntryIndexes()) { + if (entry.getParent().getNodeId().equals(parent.getNodeId())) + parent.addChildEntry(entry); + } + } - private void constructTreeFromIndex(PwGroupV3 currentGroup) { + private void constructTreeFromIndex(PwGroupV3 currentGroup) { - assignGroupsChildren(currentGroup); - assignEntriesChildren(currentGroup); + assignGroupsChildren(currentGroup); + assignEntriesChildren(currentGroup); - // set parent in child entries (normally useless but to be sure or to update parent metadata) - for (PwEntryV3 childEntry : currentGroup.getChildEntries()) { - childEntry.setParent(currentGroup); - } - // recursively construct child groups - for (PwGroupV3 childGroup : currentGroup.getChildGroups()) { - childGroup.setParent(currentGroup); - constructTreeFromIndex(childGroup); - } - } + // set parent in child entries (normally useless but to be sure or to update parent metadata) + for (PwEntryV3 childEntry : currentGroup.getChildEntries()) { + childEntry.setParent(currentGroup); + } + // recursively construct child groups + for (PwGroupV3 childGroup : currentGroup.getChildGroups()) { + childGroup.setParent(currentGroup); + constructTreeFromIndex(childGroup); + } + } - public void constructTreeFromIndex() { - constructTreeFromIndex(getRootGroup()); - } + public void constructTreeFromIndex() { + constructTreeFromIndex(getRootGroup()); + } - /** - * Generates an unused random tree id - * - * @return new tree id - */ - @Override - public PwNodeIdInt newGroupId() { - PwNodeIdInt newId; - do { - newId = new PwNodeIdInt(); - } while (isGroupIdUsed(newId)); + /** + * Generates an unused random tree id + * + * @return new tree id + */ + @Override + public PwNodeIdInt newGroupId() { + PwNodeIdInt newId; + do { + newId = new PwNodeIdInt(); + } while (isGroupIdUsed(newId)); - return newId; - } + return newId; + } - /** - * Generates an unused random tree id - * - * @return new tree id - */ - @Override - public PwNodeIdUUID newEntryId() { - PwNodeIdUUID newId; - do { - newId = new PwNodeIdUUID(); - } while (isEntryIdUsed(newId)); + /** + * Generates an unused random tree id + * + * @return new tree id + */ + @Override + public PwNodeIdUUID newEntryId() { + PwNodeIdUUID newId; + do { + newId = new PwNodeIdUUID(); + } while (isEntryIdUsed(newId)); - return newId; - } + return newId; + } - @Override - public byte[] getMasterKey(@Nullable String key, @Nullable InputStream keyInputStream) - throws InvalidKeyFileException, IOException { + @Override + public byte[] getMasterKey(@Nullable String key, @Nullable InputStream keyInputStream) + throws InvalidKeyFileException, IOException { - if (key != null && keyInputStream != null) { - return getCompositeKey(key, keyInputStream); - } else if (key != null) { // key.length() >= 0 - return getPasswordKey(key); - } else if (keyInputStream != null) { // key == null - return getFileKey(keyInputStream); - } else { - throw new IllegalArgumentException("Key cannot be empty."); - } - } + if (key != null && keyInputStream != null) { + return getCompositeKey(key, keyInputStream); + } else if (key != null) { // key.length() >= 0 + return getPasswordKey(key); + } else if (keyInputStream != null) { // key == null + return getFileKey(keyInputStream); + } else { + throw new IllegalArgumentException("Key cannot be empty."); + } + } /** * Encrypt the master key a few times to make brute-force key-search harder @@ -170,84 +170,84 @@ public class PwDatabaseV3 extends PwDatabase { return key.transformMasterKey(pKeySeed, pKey, rounds); } - public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, long numRounds) throws IOException { + public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, long numRounds) throws IOException { - // Write checksum Checksum - MessageDigest md; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IOException("SHA-256 not implemented here."); - } - NullOutputStream nos = new NullOutputStream(); - DigestOutputStream dos = new DigestOutputStream(nos, md); + // Write checksum Checksum + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not implemented here."); + } + NullOutputStream nos = new NullOutputStream(); + DigestOutputStream dos = new DigestOutputStream(nos, md); - byte[] transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds); - dos.write(masterSeed); - dos.write(transformedMasterKey); + byte[] transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds); + dos.write(masterSeed); + dos.write(transformedMasterKey); - finalKey = md.digest(); - } + finalKey = md.digest(); + } - @Override - protected String getPasswordEncoding() { - return "ISO-8859-1"; - } - - @Override - protected byte[] loadXmlKeyFile(InputStream keyInputStream) { - return null; - } + @Override + protected String getPasswordEncoding() { + return "ISO-8859-1"; + } + + @Override + protected byte[] loadXmlKeyFile(InputStream keyInputStream) { + return null; + } - @Override - public long getNumberKeyEncryptionRounds() { - return numKeyEncRounds; - } + @Override + public long getNumberKeyEncryptionRounds() { + return numKeyEncRounds; + } - @Override - public void setNumberKeyEncryptionRounds(long rounds) throws NumberFormatException { - if (rounds > Integer.MAX_VALUE || rounds < Integer.MIN_VALUE) { - throw new NumberFormatException(); - } - numKeyEncRounds = (int) rounds; - } + @Override + public void setNumberKeyEncryptionRounds(long rounds) throws NumberFormatException { + if (rounds > Integer.MAX_VALUE || rounds < Integer.MIN_VALUE) { + throw new NumberFormatException(); + } + numKeyEncRounds = (int) rounds; + } - @Override - public PwGroupV3 createGroup() { - return new PwGroupV3(); - } + @Override + public PwGroupV3 createGroup() { + return new PwGroupV3(); + } - public void setRootGroup(PwGroupV3 rootGroup) { - this.rootGroup = rootGroup; - } + public void setRootGroup(PwGroupV3 rootGroup) { + this.rootGroup = rootGroup; + } - @Override - public PwGroupV3 getRootGroup() { - return rootGroup; - } + @Override + public PwGroupV3 getRootGroup() { + return rootGroup; + } - @Override - public PwEntryV3 createEntry() { - return new PwEntryV3(); - } + @Override + public PwEntryV3 createEntry() { + return new PwEntryV3(); + } - @Override - public boolean isBackup(PwGroupV3 group) { - while (group != null) { - if (group.getLevel() == 0 && group.getTitle().equalsIgnoreCase("Backup")) { - return true; - } - group = group.getParent(); - } - return false; - } + @Override + public boolean isBackup(PwGroupV3 group) { + while (group != null) { + if (group.getLevel() == 0 && group.getTitle().equalsIgnoreCase("Backup")) { + return true; + } + group = group.getParent(); + } + return false; + } - @Override - public boolean isGroupSearchable(PwGroupV3 group, boolean omitBackup) { - if (!super.isGroupSearchable(group, omitBackup)) { - return false; - } - return !(omitBackup && isBackup(group)); - } + @Override + public boolean isGroupSearchable(PwGroupV3 group, boolean omitBackup) { + if (!super.isGroupSearchable(group, omitBackup)) { + return false; + } + return !(omitBackup && isBackup(group)); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java index 3fb4d2029..8e535faad 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -37,11 +37,11 @@ import java.util.Date; */ public class PwDate implements Parcelable { - private static final int DATE_SIZE = 5; + private static final int DATE_SIZE = 5; private Date jDate = null; - private boolean jDateBuilt = false; - transient private byte[] cDate = null; + private boolean jDateBuilt = false; + transient private byte[] cDate = null; transient private boolean cDateBuilt = false; public static final Date NEVER_EXPIRE = getNeverExpire(); @@ -73,47 +73,47 @@ public class PwDate implements Parcelable { return cal.getTime(); } - - public PwDate(byte[] buf, int offset) { - cDate = new byte[DATE_SIZE]; - System.arraycopy(buf, offset, cDate, 0, DATE_SIZE); - cDateBuilt = true; - } - public PwDate(PwDate source) { - if (source.jDate != null) { - this.jDate = new Date(source.jDate.getTime()); - } - this.jDateBuilt = source.jDateBuilt; + public PwDate(byte[] buf, int offset) { + cDate = new byte[DATE_SIZE]; + System.arraycopy(buf, offset, cDate, 0, DATE_SIZE); + cDateBuilt = true; + } - if (source.cDate != null) { - int dateLength = source.cDate.length; - this.cDate = new byte[dateLength]; - System.arraycopy(source.cDate, 0, this.cDate, 0, dateLength); - } - this.cDateBuilt = source.cDateBuilt; - } + public PwDate(PwDate source) { + if (source.jDate != null) { + this.jDate = new Date(source.jDate.getTime()); + } + this.jDateBuilt = source.jDateBuilt; - public PwDate(Date date) { - jDate = new Date(date.getTime()); - jDateBuilt = true; - } - - public PwDate(long millis) { - jDate = new Date(millis); - jDateBuilt = true; - } - - public PwDate() { - jDate = new Date(); - jDateBuilt = true; - } + if (source.cDate != null) { + int dateLength = source.cDate.length; + this.cDate = new byte[dateLength]; + System.arraycopy(source.cDate, 0, this.cDate, 0, dateLength); + } + this.cDateBuilt = source.cDateBuilt; + } - protected PwDate(Parcel in) { - jDate = (Date) in.readSerializable(); - jDateBuilt = in.readByte() != 0; + public PwDate(Date date) { + jDate = new Date(date.getTime()); + jDateBuilt = true; + } + + public PwDate(long millis) { + jDate = new Date(millis); + jDateBuilt = true; + } + + public PwDate() { + jDate = new Date(); + jDateBuilt = true; + } + + protected PwDate(Parcel in) { + jDate = (Date) in.readSerializable(); + jDateBuilt = in.readByte() != 0; cDateBuilt = false; - } + } @Override public int describeContents() { @@ -121,146 +121,146 @@ public class PwDate implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(Parcel dest, int flags) { dest.writeSerializable(getDate()); dest.writeByte((byte) (jDateBuilt ? 1 : 0)); - } + } - public static final Creator CREATOR = new Creator() { - @Override - public PwDate createFromParcel(Parcel in) { - return new PwDate(in); - } + public static final Creator CREATOR = new Creator() { + @Override + public PwDate createFromParcel(Parcel in) { + return new PwDate(in); + } - @Override - public PwDate[] newArray(int size) { - return new PwDate[size]; - } - }; + @Override + public PwDate[] newArray(int size) { + return new PwDate[size]; + } + }; - public Date getDate() { - if ( ! jDateBuilt ) { - jDate = readTime(cDate, 0, App.getCalendar()); - jDateBuilt = true; - } - - return jDate; - } - - public byte[] getCDate() { - if ( ! cDateBuilt ) { - cDate = writeTime(jDate, App.getCalendar()); - cDateBuilt = true; - } - - return cDate; - } - - /** - * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked - * to a java.util.Date instance. - */ - public static Date readTime(byte[] buf, int offset, Calendar time) { - int dw1 = Types.readUByte(buf, offset); - int dw2 = Types.readUByte(buf, offset + 1); - int dw3 = Types.readUByte(buf, offset + 2); - int dw4 = Types.readUByte(buf, offset + 3); - int dw5 = Types.readUByte(buf, offset + 4); + public Date getDate() { + if ( ! jDateBuilt ) { + jDate = readTime(cDate, 0, App.getCalendar()); + jDateBuilt = true; + } - // Unpack 5 byte structure to date and time - int year = (dw1 << 6) | (dw2 >> 2); - int month = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); + return jDate; + } - int day = (dw3 >> 1) & 0x0000001F; - int hour = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); - int minute = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); - int second = dw5 & 0x0000003F; + public byte[] getCDate() { + if ( ! cDateBuilt ) { + cDate = writeTime(jDate, App.getCalendar()); + cDateBuilt = true; + } - if (time == null) { - time = Calendar.getInstance(); - } - // File format is a 1 based month, java Calendar uses a zero based month - // File format is a 1 based day, java Calendar uses a 1 based day - time.set(year, month - 1, day, hour, minute, second); + return cDate; + } - return time.getTime(); + /** + * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked + * to a java.util.Date instance. + */ + public static Date readTime(byte[] buf, int offset, Calendar time) { + int dw1 = Types.readUByte(buf, offset); + int dw2 = Types.readUByte(buf, offset + 1); + int dw3 = Types.readUByte(buf, offset + 2); + int dw4 = Types.readUByte(buf, offset + 3); + int dw5 = Types.readUByte(buf, offset + 4); - } + // Unpack 5 byte structure to date and time + int year = (dw1 << 6) | (dw2 >> 2); + int month = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); - public static byte[] writeTime(Date date) { - return writeTime(date, null); - } - - public static byte[] writeTime(Date date, Calendar cal) { - if (date == null) { - return null; - } + int day = (dw3 >> 1) & 0x0000001F; + int hour = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); + int minute = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); + int second = dw5 & 0x0000003F; - byte[] buf = new byte[5]; - if (cal == null) { - cal = Calendar.getInstance(); - } - cal.setTime(date); + if (time == null) { + time = Calendar.getInstance(); + } + // File format is a 1 based month, java Calendar uses a zero based month + // File format is a 1 based day, java Calendar uses a 1 based day + time.set(year, month - 1, day, hour, minute, second); - int year = cal.get(Calendar.YEAR); - // File format is a 1 based month, java Calendar uses a zero based month - int month = cal.get(Calendar.MONTH) + 1; - // File format is a 0 based day, java Calendar uses a 1 based day - int day = cal.get(Calendar.DAY_OF_MONTH) - 1; - int hour = cal.get(Calendar.HOUR_OF_DAY); - int minute = cal.get(Calendar.MINUTE); - int second = cal.get(Calendar.SECOND); + return time.getTime(); - buf[0] = Types.writeUByte(((year >> 6) & 0x0000003F)); - buf[1] = Types.writeUByte(((year & 0x0000003F) << 2) - | ((month >> 2) & 0x00000003)); - buf[2] = (byte) (((month & 0x00000003) << 6) - | ((day & 0x0000001F) << 1) | ((hour >> 4) & 0x00000001)); - buf[3] = (byte) (((hour & 0x0000000F) << 4) | ((minute >> 2) & 0x0000000F)); - buf[4] = (byte) (((minute & 0x00000003) << 6) | (second & 0x0000003F)); + } - return buf; - } + public static byte[] writeTime(Date date) { + return writeTime(date, null); + } - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null ) { - return false; - } - if ( getClass() != o.getClass() ) { - return false; - } - - PwDate date = (PwDate) o; - if ( cDateBuilt && date.cDateBuilt ) { - return Arrays.equals(cDate, date.cDate); - } else if ( jDateBuilt && date.jDateBuilt ) { - return IsSameDate(jDate, date.jDate); - } else if ( cDateBuilt && date.jDateBuilt ) { - return Arrays.equals(date.getCDate(), cDate); - } else { - return IsSameDate(date.getDate(), jDate); - } - } + public static byte[] writeTime(Date date, Calendar cal) { + if (date == null) { + return null; + } - private static boolean IsSameDate(Date d1, Date d2) { - Calendar cal1 = Calendar.getInstance(); - cal1.setTime(d1); - cal1.set(Calendar.MILLISECOND, 0); - - Calendar cal2 = Calendar.getInstance(); - cal2.setTime(d2); - cal2.set(Calendar.MILLISECOND, 0); - - return (cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)) && - (cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH)) && - (cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH)) && - (cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR)) && - (cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE)) && - (cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)); - - } + byte[] buf = new byte[5]; + if (cal == null) { + cal = Calendar.getInstance(); + } + cal.setTime(date); + + int year = cal.get(Calendar.YEAR); + // File format is a 1 based month, java Calendar uses a zero based month + int month = cal.get(Calendar.MONTH) + 1; + // File format is a 0 based day, java Calendar uses a 1 based day + int day = cal.get(Calendar.DAY_OF_MONTH) - 1; + int hour = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + int second = cal.get(Calendar.SECOND); + + buf[0] = Types.writeUByte(((year >> 6) & 0x0000003F)); + buf[1] = Types.writeUByte(((year & 0x0000003F) << 2) + | ((month >> 2) & 0x00000003)); + buf[2] = (byte) (((month & 0x00000003) << 6) + | ((day & 0x0000001F) << 1) | ((hour >> 4) & 0x00000001)); + buf[3] = (byte) (((hour & 0x0000000F) << 4) | ((minute >> 2) & 0x0000000F)); + buf[4] = (byte) (((minute & 0x00000003) << 6) | (second & 0x0000003F)); + + return buf; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null ) { + return false; + } + if ( getClass() != o.getClass() ) { + return false; + } + + PwDate date = (PwDate) o; + if ( cDateBuilt && date.cDateBuilt ) { + return Arrays.equals(cDate, date.cDate); + } else if ( jDateBuilt && date.jDateBuilt ) { + return IsSameDate(jDate, date.jDate); + } else if ( cDateBuilt && date.jDateBuilt ) { + return Arrays.equals(date.getCDate(), cDate); + } else { + return IsSameDate(date.getDate(), jDate); + } + } + + private static boolean IsSameDate(Date d1, Date d2) { + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(d1); + cal1.set(Calendar.MILLISECOND, 0); + + Calendar cal2 = Calendar.getInstance(); + cal2.setTime(d2); + cal2.set(Calendar.MILLISECOND, 0); + + return (cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)) && + (cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH)) && + (cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH)) && + (cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR)) && + (cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE)) && + (cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)); + + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java index 8cb52d41f..3f819093c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -45,16 +45,16 @@ import com.kunzisoft.keepass.stylish.StylishActivity; public class IconPickerDialogFragment extends DialogFragment { - public static final String KEY_ICON_STANDARD = "KEY_ICON_STANDARD"; + public static final String KEY_ICON_STANDARD = "KEY_ICON_STANDARD"; - private IconPickerListener iconPickerListener; - private IconPack iconPack; + private IconPickerListener iconPickerListener; + private IconPack iconPack; - public static void launch(StylishActivity activity) { + public static void launch(StylishActivity activity) { // Create an instance of the dialog fragment and show it IconPickerDialogFragment dialog = new IconPickerDialogFragment(); dialog.show(activity.getSupportFragmentManager(), "IconPickerDialogFragment"); - } + } @Override public void onAttach(Context context) { @@ -66,86 +66,86 @@ public class IconPickerDialogFragment extends DialogFragment { throw new ClassCastException(context.toString() + " must implement " + IconPickerListener.class.getName()); } - } + } - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - // Get the layout inflater - LayoutInflater inflater = getActivity().getLayoutInflater(); + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + // Get the layout inflater + LayoutInflater inflater = getActivity().getLayoutInflater(); - iconPack = IconPackChooser.getSelectedIconPack(getContext()); + iconPack = IconPackChooser.getSelectedIconPack(getContext()); - // Inflate and set the layout for the dialog - // Pass null as the parent view because its going in the dialog layout - View root = inflater.inflate(R.layout.icon_picker, null); - builder.setView(root); + // Inflate and set the layout for the dialog + // Pass null as the parent view because its going in the dialog layout + View root = inflater.inflate(R.layout.icon_picker, null); + builder.setView(root); - GridView currIconGridView = root.findViewById(R.id.IconGridView); - currIconGridView.setAdapter(new ImageAdapter(this.getContext())); + GridView currIconGridView = root.findViewById(R.id.IconGridView); + currIconGridView.setAdapter(new ImageAdapter(this.getContext())); - currIconGridView.setOnItemClickListener((parent, v, position, id) -> { + currIconGridView.setOnItemClickListener((parent, v, position, id) -> { Bundle bundle = new Bundle(); - bundle.putParcelable(KEY_ICON_STANDARD, new PwIconStandard(position)); - iconPickerListener.iconPicked(bundle); + bundle.putParcelable(KEY_ICON_STANDARD, new PwIconStandard(position)); + iconPickerListener.iconPicked(bundle); dismiss(); }); builder.setNegativeButton(R.string.cancel, (dialog, id) -> - IconPickerDialogFragment.this.getDialog().cancel()); + IconPickerDialogFragment.this.getDialog().cancel()); - return builder.create(); - } - - public class ImageAdapter extends BaseAdapter { - private Context context; + return builder.create(); + } - ImageAdapter(Context c) { - context = c; - } + public class ImageAdapter extends BaseAdapter { + private Context context; - public int getCount() { - /* Return number of KeePass icons */ - return iconPack.numberOfIcons(); - } - - public Object getItem(int position) { - return null; - } + ImageAdapter(Context c) { + context = c; + } - public long getItemId(int position) { - return 0; - } - - public View getView(int position, View convertView, ViewGroup parent) { - View currView; - if(convertView == null) { - LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + public int getCount() { + /* Return number of KeePass icons */ + return iconPack.numberOfIcons(); + } + + public Object getItem(int position) { + return null; + } + + public long getItemId(int position) { + return 0; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View currView; + if(convertView == null) { + LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); assert li != null; currView = li.inflate(R.layout.icon, parent, false); - } - else { - currView = convertView; - } - ImageView iv = currView.findViewById(R.id.icon_image); - iv.setImageResource(iconPack.iconToResId(position)); + } + else { + currView = convertView; + } + ImageView iv = currView.findViewById(R.id.icon_image); + iv.setImageResource(iconPack.iconToResId(position)); - // Assign color if icons are tintable - if (iconPack.tintable()) { - // Retrieve the textColor to tint the icon - int[] attrs = {android.R.attr.textColor}; - assert getContext() != null; - TypedArray ta = getContext().getTheme().obtainStyledAttributes(attrs); - int iconColor = ta.getColor(0, Color.BLACK); + // Assign color if icons are tintable + if (iconPack.tintable()) { + // Retrieve the textColor to tint the icon + int[] attrs = {android.R.attr.textColor}; + assert getContext() != null; + TypedArray ta = getContext().getTheme().obtainStyledAttributes(attrs); + int iconColor = ta.getColor(0, Color.BLACK); ImageViewCompat.setImageTintList(iv, ColorStateList.valueOf(iconColor)); - } + } - return currView; - } - } + return currView; + } + } - public interface IconPickerListener { - void iconPicked(Bundle bundle); - } + public interface IconPickerListener { + void iconPicked(Bundle bundle); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/ReadOnlyDialog.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/ReadOnlyDialog.java index a237cd052..014c38444 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/ReadOnlyDialog.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/ReadOnlyDialog.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -25,14 +25,14 @@ import android.os.Build; import com.kunzisoft.keepass.R; public class ReadOnlyDialog extends WarningDialog { - - public ReadOnlyDialog(Context context) { - super(context, R.string.show_read_only_warning); - - warning = context.getString(R.string.read_only_warning); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - warning = warning.concat("\n\n").concat(context.getString(R.string.read_only_kitkat_warning)); - } - } + + public ReadOnlyDialog(Context context) { + super(context, R.string.show_read_only_warning); + + warning = context.getString(R.string.read_only_warning); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + warning = warning.concat("\n\n").concat(context.getString(R.string.read_only_kitkat_warning)); + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/WarningDialog.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/WarningDialog.java index 7511672fe..27dde376b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/WarningDialog.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/WarningDialog.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -29,50 +29,50 @@ import android.preference.PreferenceManager; import com.kunzisoft.keepass.R; public class WarningDialog extends AlertDialog { - - protected String warning; - private int showKey; - public WarningDialog(Context context, int dontShowKey) { - super(context); - - this.showKey = dontShowKey; - } - - public WarningDialog(Context context, int warningKey, int dontShowKey) { - this(context, dontShowKey); + protected String warning; + private int showKey; - warning = context.getString(warningKey); - } + public WarningDialog(Context context, int dontShowKey) { + super(context); - @Override - protected void onCreate(Bundle savedInstanceState) { - Context ctx = getContext(); - setMessage(warning); - - setButton(AlertDialog.BUTTON1, ctx.getText(android.R.string.ok), new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - dismiss(); - } - }); - - setButton(AlertDialog.BUTTON2, ctx.getText(R.string.beta_dontask), new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - Context ctx = getContext(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - SharedPreferences.Editor edit = prefs.edit(); - edit.putBoolean(ctx.getString(showKey), false); - edit.commit(); - - dismiss(); - } - }); - - super.onCreate(savedInstanceState); - } + this.showKey = dontShowKey; + } + + public WarningDialog(Context context, int warningKey, int dontShowKey) { + this(context, dontShowKey); + + warning = context.getString(warningKey); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + Context ctx = getContext(); + setMessage(warning); + + setButton(AlertDialog.BUTTON1, ctx.getText(android.R.string.ok), new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + dismiss(); + } + }); + + setButton(AlertDialog.BUTTON2, ctx.getText(R.string.beta_dontask), new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + Context ctx = getContext(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); + SharedPreferences.Editor edit = prefs.edit(); + edit.putBoolean(ctx.getString(showKey), false); + edit.commit(); + + dismiss(); + } + }); + + super.onCreate(savedInstanceState); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/BrowserDialog.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/BrowserDialog.java index 4ded1e954..d62e1f23e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/BrowserDialog.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/BrowserDialog.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -33,29 +33,29 @@ import com.kunzisoft.keepass.utils.Util; public class BrowserDialog extends DialogFragment { - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - // Get the layout inflater - LayoutInflater inflater = getActivity().getLayoutInflater(); - View root = inflater.inflate(R.layout.browser_install, null); - builder.setView(root) - .setNegativeButton(R.string.cancel, (dialog, id) -> { }); + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + // Get the layout inflater + LayoutInflater inflater = getActivity().getLayoutInflater(); + View root = inflater.inflate(R.layout.browser_install, null); + builder.setView(root) + .setNegativeButton(R.string.cancel, (dialog, id) -> { }); - Button market = root.findViewById(R.id.install_market); - market.setOnClickListener((view) -> { - Util.gotoUrl(getContext(), R.string.filemanager_play_store); - dismiss(); - }); + Button market = root.findViewById(R.id.install_market); + market.setOnClickListener((view) -> { + Util.gotoUrl(getContext(), R.string.filemanager_play_store); + dismiss(); + }); - Button web = root.findViewById(R.id.install_web); - web.setOnClickListener(view -> { + Button web = root.findViewById(R.id.install_web); + web.setOnClickListener(view -> { Util.gotoUrl(getContext(), R.string.filemanager_f_droid); dismiss(); }); - return builder.create(); - } + return builder.create(); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDbHelper.java index 17d320578..2738173b6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDbHelper.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -34,221 +34,221 @@ import java.io.FileFilter; public class FileDbHelper { private static final String TAG = FileDbHelper.class.getName(); - - public static final String LAST_FILENAME = "lastFile"; - public static final String LAST_KEYFILE = "lastKey"; - - public static final String DATABASE_NAME = "keepassdroid"; // TODO Change db name - private static final String FILE_TABLE = "files"; - private static final int DATABASE_VERSION = 1; - - public static final int MAX_FILES = 5; - - public static final String KEY_FILE_ID = "_id"; - public static final String KEY_FILE_FILENAME = "fileName"; - public static final String KEY_FILE_KEYFILE = "keyFile"; - public static final String KEY_FILE_UPDATED = "updated"; - private static final String DATABASE_CREATE = - "create table " + FILE_TABLE + " ( " + KEY_FILE_ID + " integer primary key autoincrement, " - + KEY_FILE_FILENAME + " text not null, " + KEY_FILE_KEYFILE + " text, " - + KEY_FILE_UPDATED + " integer not null);"; - - private final Context mCtx; - private DatabaseHelper mDbHelper; - private SQLiteDatabase mDb; - - private static class DatabaseHelper extends SQLiteOpenHelper { - private final Context mCtx; - - DatabaseHelper(Context ctx) { - super(ctx, DATABASE_NAME, null, DATABASE_VERSION); - mCtx = ctx; - } + public static final String LAST_FILENAME = "lastFile"; + public static final String LAST_KEYFILE = "lastKey"; - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(DATABASE_CREATE); - - // Migrate preference to database if it is set. - SharedPreferences settings = mCtx.getSharedPreferences("PasswordActivity", Context.MODE_PRIVATE); - String lastFile = settings.getString(LAST_FILENAME, ""); - String lastKey = settings.getString(LAST_KEYFILE,""); - - if ( lastFile.length() > 0 ) { - ContentValues vals = new ContentValues(); - vals.put(KEY_FILE_FILENAME, lastFile); - vals.put(KEY_FILE_UPDATED, System.currentTimeMillis()); - - if ( lastKey.length() > 0 ) { - vals.put(KEY_FILE_KEYFILE, lastKey); - } - - db.insert(FILE_TABLE, null, vals); - - // Clear old preferences - deletePrefs(settings); - - } - } + public static final String DATABASE_NAME = "keepassdroid"; // TODO Change db name + private static final String FILE_TABLE = "files"; + private static final int DATABASE_VERSION = 1; + + public static final int MAX_FILES = 5; + + public static final String KEY_FILE_ID = "_id"; + public static final String KEY_FILE_FILENAME = "fileName"; + public static final String KEY_FILE_KEYFILE = "keyFile"; + public static final String KEY_FILE_UPDATED = "updated"; + + private static final String DATABASE_CREATE = + "create table " + FILE_TABLE + " ( " + KEY_FILE_ID + " integer primary key autoincrement, " + + KEY_FILE_FILENAME + " text not null, " + KEY_FILE_KEYFILE + " text, " + + KEY_FILE_UPDATED + " integer not null);"; + + private final Context mCtx; + private DatabaseHelper mDbHelper; + private SQLiteDatabase mDb; + + private static class DatabaseHelper extends SQLiteOpenHelper { + private final Context mCtx; + + DatabaseHelper(Context ctx) { + super(ctx, DATABASE_NAME, null, DATABASE_VERSION); + mCtx = ctx; + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(DATABASE_CREATE); + + // Migrate preference to database if it is set. + SharedPreferences settings = mCtx.getSharedPreferences("PasswordActivity", Context.MODE_PRIVATE); + String lastFile = settings.getString(LAST_FILENAME, ""); + String lastKey = settings.getString(LAST_KEYFILE,""); + + if ( lastFile.length() > 0 ) { + ContentValues vals = new ContentValues(); + vals.put(KEY_FILE_FILENAME, lastFile); + vals.put(KEY_FILE_UPDATED, System.currentTimeMillis()); + + if ( lastKey.length() > 0 ) { + vals.put(KEY_FILE_KEYFILE, lastKey); + } + + db.insert(FILE_TABLE, null, vals); + + // Clear old preferences + deletePrefs(settings); + + } + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // Only one database version so far + } + + private void deletePrefs(SharedPreferences prefs) { + // We won't worry too much if this fails + try { + SharedPreferences.Editor editor = prefs.edit(); + editor.remove(LAST_FILENAME); + editor.remove(LAST_KEYFILE); + editor.apply(); + } catch (Exception e) { + Log.e(TAG, "Unable to delete database preference", e); + } + } + } + + public FileDbHelper(Context ctx) { + mCtx = ctx; + } + + public FileDbHelper open() throws SQLException { + mDbHelper = new DatabaseHelper(mCtx); + mDb = mDbHelper.getWritableDatabase(); + return this; + } + + public boolean isOpen() { + return mDb.isOpen(); + } + + public void close() { + mDb.close(); + } + + public long createFile(String fileName, String keyFile) { + + // Check to see if this filename is already used + Cursor cursor; + try { + cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_ID}, + KEY_FILE_FILENAME + "=?", new String[] {fileName}, null, null, null, null); + } catch (Exception e ) { + assert(true); + return -1; + } + + long result; + // If there is an existing entry update it with the new key file + if ( cursor.getCount() > 0 ) { + cursor.moveToFirst(); + long id = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_FILE_ID)); + + ContentValues vals = new ContentValues(); + vals.put(KEY_FILE_KEYFILE, keyFile); + vals.put(KEY_FILE_UPDATED, System.currentTimeMillis()); + + result = mDb.update(FILE_TABLE, vals, KEY_FILE_ID + " = " + id, null); + + // Otherwise add the new entry + } else { + ContentValues vals = new ContentValues(); + vals.put(KEY_FILE_FILENAME, fileName); + vals.put(KEY_FILE_KEYFILE, keyFile); + vals.put(KEY_FILE_UPDATED, System.currentTimeMillis()); + + result = mDb.insert(FILE_TABLE, null, vals); + + } + // Delete all but the last five records + try { + deleteAllBut(MAX_FILES); + } catch (Exception e) { + e.printStackTrace(); + assert(true); + } + + cursor.close(); + + return result; + + } + + private void deleteAllBut(int limit) { + Cursor cursor = mDb.query(FILE_TABLE, new String[] {KEY_FILE_UPDATED}, null, null, null, null, KEY_FILE_UPDATED); + + if ( cursor.getCount() > limit ) { + cursor.moveToFirst(); + long time = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_FILE_UPDATED)); + + mDb.execSQL("DELETE FROM " + FILE_TABLE + " WHERE " + KEY_FILE_UPDATED + "<" + time + ";"); + } + + cursor.close(); + + } + + public void deleteAllKeys() { + ContentValues vals = new ContentValues(); + vals.put(KEY_FILE_KEYFILE, ""); + + mDb.update(FILE_TABLE, vals, null, null); + } + + public void deleteFile(String filename) { + mDb.delete(FILE_TABLE, KEY_FILE_FILENAME + " = ?", new String[] {filename}); + } + + + public Cursor fetchAllFiles() { + Cursor ret; + ret = mDb.query(FILE_TABLE, new String[] {KEY_FILE_ID, KEY_FILE_FILENAME, KEY_FILE_KEYFILE }, null, null, null, null, KEY_FILE_UPDATED + " DESC", Integer.toString(MAX_FILES)); + return ret; + } + + public Cursor fetchFile(long fileId) throws SQLException { + Cursor cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_FILENAME, KEY_FILE_KEYFILE}, + KEY_FILE_ID + "=" + fileId, null, null, null, null, null); + + if ( cursor != null ) { + cursor.moveToFirst(); + } + + return cursor; + + } + + public String getFileByName(String name) { + Cursor cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_KEYFILE}, + KEY_FILE_FILENAME + "= ?", new String[] {name}, null, null, null, null); + + if ( cursor == null ) { + return ""; + } + + String filename; + + if ( cursor.moveToFirst() ) { + filename = cursor.getString(0); + } else { + // Cursor is empty + filename = ""; + } + cursor.close(); + return filename; + } + + public boolean hasRecentFiles() { + Cursor cursor = fetchAllFiles(); + + boolean hasRecent = cursor.getCount() > 0; + cursor.close(); + + return hasRecent; + } - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // Only one database version so far - } - - private void deletePrefs(SharedPreferences prefs) { - // We won't worry too much if this fails - try { - SharedPreferences.Editor editor = prefs.edit(); - editor.remove(LAST_FILENAME); - editor.remove(LAST_KEYFILE); - editor.apply(); - } catch (Exception e) { - Log.e(TAG, "Unable to delete database preference", e); - } - } - } - - public FileDbHelper(Context ctx) { - mCtx = ctx; - } - - public FileDbHelper open() throws SQLException { - mDbHelper = new DatabaseHelper(mCtx); - mDb = mDbHelper.getWritableDatabase(); - return this; - } - - public boolean isOpen() { - return mDb.isOpen(); - } - - public void close() { - mDb.close(); - } - - public long createFile(String fileName, String keyFile) { - - // Check to see if this filename is already used - Cursor cursor; - try { - cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_ID}, - KEY_FILE_FILENAME + "=?", new String[] {fileName}, null, null, null, null); - } catch (Exception e ) { - assert(true); - return -1; - } - - long result; - // If there is an existing entry update it with the new key file - if ( cursor.getCount() > 0 ) { - cursor.moveToFirst(); - long id = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_FILE_ID)); - - ContentValues vals = new ContentValues(); - vals.put(KEY_FILE_KEYFILE, keyFile); - vals.put(KEY_FILE_UPDATED, System.currentTimeMillis()); - - result = mDb.update(FILE_TABLE, vals, KEY_FILE_ID + " = " + id, null); - - // Otherwise add the new entry - } else { - ContentValues vals = new ContentValues(); - vals.put(KEY_FILE_FILENAME, fileName); - vals.put(KEY_FILE_KEYFILE, keyFile); - vals.put(KEY_FILE_UPDATED, System.currentTimeMillis()); - - result = mDb.insert(FILE_TABLE, null, vals); - - } - // Delete all but the last five records - try { - deleteAllBut(MAX_FILES); - } catch (Exception e) { - e.printStackTrace(); - assert(true); - } - - cursor.close(); - - return result; - - } - - private void deleteAllBut(int limit) { - Cursor cursor = mDb.query(FILE_TABLE, new String[] {KEY_FILE_UPDATED}, null, null, null, null, KEY_FILE_UPDATED); - - if ( cursor.getCount() > limit ) { - cursor.moveToFirst(); - long time = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_FILE_UPDATED)); - - mDb.execSQL("DELETE FROM " + FILE_TABLE + " WHERE " + KEY_FILE_UPDATED + "<" + time + ";"); - } - - cursor.close(); - - } - - public void deleteAllKeys() { - ContentValues vals = new ContentValues(); - vals.put(KEY_FILE_KEYFILE, ""); - - mDb.update(FILE_TABLE, vals, null, null); - } - - public void deleteFile(String filename) { - mDb.delete(FILE_TABLE, KEY_FILE_FILENAME + " = ?", new String[] {filename}); - } - - - public Cursor fetchAllFiles() { - Cursor ret; - ret = mDb.query(FILE_TABLE, new String[] {KEY_FILE_ID, KEY_FILE_FILENAME, KEY_FILE_KEYFILE }, null, null, null, null, KEY_FILE_UPDATED + " DESC", Integer.toString(MAX_FILES)); - return ret; - } - - public Cursor fetchFile(long fileId) throws SQLException { - Cursor cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_FILENAME, KEY_FILE_KEYFILE}, - KEY_FILE_ID + "=" + fileId, null, null, null, null, null); - - if ( cursor != null ) { - cursor.moveToFirst(); - } - - return cursor; - - } - - public String getFileByName(String name) { - Cursor cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_KEYFILE}, - KEY_FILE_FILENAME + "= ?", new String[] {name}, null, null, null, null); - - if ( cursor == null ) { - return ""; - } - - String filename; - - if ( cursor.moveToFirst() ) { - filename = cursor.getString(0); - } else { - // Cursor is empty - filename = ""; - } - cursor.close(); - return filename; - } - - public boolean hasRecentFiles() { - Cursor cursor = fetchAllFiles(); - - boolean hasRecent = cursor.getCount() > 0; - cursor.close(); - - return hasRecent; - } - /** * Deletes a database including its journal file and other auxiliary files * that may have been created by the database engine. @@ -257,7 +257,7 @@ public class FileDbHelper { * @return True if the database was successfully deleted. */ public static boolean deleteDatabase(Context ctx) { - File file = ctx.getDatabasePath(DATABASE_NAME); + File file = ctx.getDatabasePath(DATABASE_NAME); if (file == null) { throw new IllegalArgumentException("file must not be null"); } diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java index 5430c444c..1c7d4c2ea 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -86,7 +86,7 @@ import permissions.dispatcher.RuntimePermissions; public class FileSelectActivity extends StylishActivity implements CreateFileDialogFragment.DefinePathDialogListener, AssignMasterKeyDialogFragment.AssignPasswordDialogListener, - FileSelectAdapter.FileItemOpenListener, + FileSelectAdapter.FileItemOpenListener, FileSelectAdapter.FileSelectClearListener, FileSelectAdapter.FileInformationShowListener { @@ -95,64 +95,64 @@ public class FileSelectActivity extends StylishActivity implements private static final String EXTRA_STAY = "EXTRA_STAY"; private FileSelectAdapter mAdapter; - private View fileListContainer; - private View createButtonView; - private View browseButtonView; - private View openButtonView; + private View fileListContainer; + private View createButtonView; + private View browseButtonView; + private View openButtonView; - private RecentFileHistory fileHistory; + private RecentFileHistory fileHistory; private View fileSelectExpandableButton; private ExpandableLayout fileSelectExpandable; - private EditText openFileNameView; + private EditText openFileNameView; - private Uri databaseUri; + private Uri databaseUri; - private KeyFileHelper keyFileHelper; + private KeyFileHelper keyFileHelper; private String defaultPath; - /* - * ------------------------- - * No Standard Launch, pass by PasswordActivity - * ------------------------- - */ + /* + * ------------------------- + * No Standard Launch, pass by PasswordActivity + * ------------------------- + */ - /* - * ------------------------- - * Keyboard Launch - * ------------------------- - */ + /* + * ------------------------- + * Keyboard Launch + * ------------------------- + */ public static void launchForKeyboardSelection(Activity activity) { - KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, new Intent(activity, FileSelectActivity.class)); + KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, new Intent(activity, FileSelectActivity.class)); } - /* - * ------------------------- - * Autofill Launch - * ------------------------- - */ + /* + * ------------------------- + * Autofill Launch + * ------------------------- + */ - @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure) { - AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, - new Intent(activity, FileSelectActivity.class), - assistStructure); - } + @RequiresApi(api = Build.VERSION_CODES.O) + public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure) { + AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, + new Intent(activity, FileSelectActivity.class), + assistStructure); + } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - fileHistory = App.getFileHistory(); + fileHistory = App.getFileHistory(); setContentView(R.layout.file_selection); fileListContainer = findViewById(R.id.container_file_list); - Toolbar toolbar = findViewById(R.id.toolbar); - toolbar.setTitle(""); - setSupportActionBar(toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); + toolbar.setTitle(""); + setSupportActionBar(toolbar); openFileNameView = findViewById(R.id.file_filename); @@ -175,37 +175,37 @@ public class FileSelectActivity extends StylishActivity implements // History list RecyclerView mListFiles = findViewById(R.id.file_list); - mListFiles.setLayoutManager(new LinearLayoutManager(this)); + mListFiles.setLayoutManager(new LinearLayoutManager(this)); - // Open button - openButtonView = findViewById(R.id.open_database); + // Open button + openButtonView = findViewById(R.id.open_database); openButtonView.setOnClickListener(v -> { - String fileName = openFileNameView.getText().toString(); + String fileName = openFileNameView.getText().toString(); if (fileName.isEmpty()) fileName = defaultPath; launchPasswordActivityWithPath(fileName); }); - // Create button - createButtonView = findViewById(R.id.create_database); + // Create button + createButtonView = findViewById(R.id.create_database); createButtonView .setOnClickListener(v -> FileSelectActivityPermissionsDispatcher .openCreateFileDialogFragmentWithPermissionCheck(FileSelectActivity.this) - ); + ); keyFileHelper = new KeyFileHelper(this); - browseButtonView = findViewById(R.id.browse_button); + browseButtonView = findViewById(R.id.browse_button); browseButtonView.setOnClickListener(keyFileHelper.getOpenFileOnClickViewListener( () -> Uri.parse("file://" + openFileNameView.getText().toString()))); - // Construct adapter with listeners - mAdapter = new FileSelectAdapter(FileSelectActivity.this, fileHistory.getDbList()); - mAdapter.setOnItemClickListener(this); - mAdapter.setFileSelectClearListener(this); - mAdapter.setFileInformationShowListener(this); - mListFiles.setAdapter(mAdapter); + // Construct adapter with listeners + mAdapter = new FileSelectAdapter(FileSelectActivity.this, fileHistory.getDbList()); + mAdapter.setOnItemClickListener(this); + mAdapter.setFileSelectClearListener(this); + mAdapter.setFileInformationShowListener(this); + mListFiles.setAdapter(mAdapter); - // Load default database if not an orientation change + // Load default database if not an orientation change if (! (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_STAY) && savedInstanceState.getBoolean(EXTRA_STAY, false)) ) { @@ -234,55 +234,55 @@ public class FileSelectActivity extends StylishActivity implements // For the first time show education checkAndPerformedEducation(); - } + } - private void fileNoFoundAction(FileNotFoundException e) { - String error = getString(R.string.file_not_found_content); - Toast.makeText(FileSelectActivity.this, - error, Toast.LENGTH_LONG).show(); - Log.e(TAG, error, e); - } + private void fileNoFoundAction(FileNotFoundException e) { + String error = getString(R.string.file_not_found_content); + Toast.makeText(FileSelectActivity.this, + error, Toast.LENGTH_LONG).show(); + Log.e(TAG, error, e); + } - private void launchPasswordActivity(String fileName, String keyFile) { - EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), - () -> { - try { - PasswordActivity.launch(FileSelectActivity.this, - fileName, keyFile); - } catch (FileNotFoundException e) { - fileNoFoundAction(e); - } - return null; - }, - () -> { - try { - PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, - fileName, keyFile); - finish(); - } catch (FileNotFoundException e) { - fileNoFoundAction(e); - } - return null; - }, - assistStructure -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - try { - PasswordActivity.launchForAutofillResult(FileSelectActivity.this, - fileName, keyFile, - assistStructure); - } catch (FileNotFoundException e) { - fileNoFoundAction(e); - } - } - return null; - }); - } + private void launchPasswordActivity(String fileName, String keyFile) { + EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), + () -> { + try { + PasswordActivity.launch(FileSelectActivity.this, + fileName, keyFile); + } catch (FileNotFoundException e) { + fileNoFoundAction(e); + } + return null; + }, + () -> { + try { + PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, + fileName, keyFile); + finish(); + } catch (FileNotFoundException e) { + fileNoFoundAction(e); + } + return null; + }, + assistStructure -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + PasswordActivity.launchForAutofillResult(FileSelectActivity.this, + fileName, keyFile, + assistStructure); + } catch (FileNotFoundException e) { + fileNoFoundAction(e); + } + } + return null; + }); + } - private void launchPasswordActivityWithPath(String path) { - launchPasswordActivity(path, ""); - // Delete flickering for kitkat <= - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) - overridePendingTransition(0, 0); + private void launchPasswordActivityWithPath(String path) { + launchPasswordActivity(path, ""); + // Delete flickering for kitkat <= + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + overridePendingTransition(0, 0); } private void updateExternalStorageWarning() { @@ -440,10 +440,10 @@ public class FileSelectActivity extends StylishActivity implements createFileDialogFragment.show(getSupportFragmentManager(), "createFileDialogFragment"); } - private void updateFileListVisibility() { - if(mAdapter.getItemCount() == 0) + private void updateFileListVisibility() { + if(mAdapter.getItemCount() == 0) fileListContainer.setVisibility(View.INVISIBLE); - else + else fileListContainer.setVisibility(View.VISIBLE); } @@ -451,64 +451,64 @@ public class FileSelectActivity extends StylishActivity implements * Create file for database * @return If not created, return false */ - private boolean createDatabaseFile(Uri path) { + private boolean createDatabaseFile(Uri path) { - String pathString = URLDecoder.decode(path.getPath()); - // Make sure file name exists - if (pathString.length() == 0) { - Log.e(TAG, getString(R.string.error_filename_required)); - Toast.makeText(FileSelectActivity.this, - R.string.error_filename_required, - Toast.LENGTH_LONG).show(); - return false; - } + String pathString = URLDecoder.decode(path.getPath()); + // Make sure file name exists + if (pathString.length() == 0) { + Log.e(TAG, getString(R.string.error_filename_required)); + Toast.makeText(FileSelectActivity.this, + R.string.error_filename_required, + Toast.LENGTH_LONG).show(); + return false; + } - // Try to create the file - File file = new File(pathString); - try { - if (file.exists()) { + // Try to create the file + File file = new File(pathString); + try { + if (file.exists()) { Log.e(TAG, getString(R.string.error_database_exists) + " " + file); - Toast.makeText(FileSelectActivity.this, - R.string.error_database_exists, - Toast.LENGTH_LONG).show(); - return false; - } - File parent = file.getParentFile(); + Toast.makeText(FileSelectActivity.this, + R.string.error_database_exists, + Toast.LENGTH_LONG).show(); + return false; + } + File parent = file.getParentFile(); - if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) { + if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) { Log.e(TAG, getString(R.string.error_invalid_path) + " " + file); - Toast.makeText(FileSelectActivity.this, - R.string.error_invalid_path, - Toast.LENGTH_LONG).show(); - return false; - } + Toast.makeText(FileSelectActivity.this, + R.string.error_invalid_path, + Toast.LENGTH_LONG).show(); + return false; + } - if ( ! parent.exists() ) { - // Create parent directory - if ( ! parent.mkdirs() ) { + if ( ! parent.exists() ) { + // Create parent directory + if ( ! parent.mkdirs() ) { Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent); - Toast.makeText(FileSelectActivity.this, - R.string.error_could_not_create_parent, - Toast.LENGTH_LONG).show(); - return false; - } - } + Toast.makeText(FileSelectActivity.this, + R.string.error_could_not_create_parent, + Toast.LENGTH_LONG).show(); + return false; + } + } - return file.createNewFile(); - } catch (IOException e) { + return file.createNewFile(); + } catch (IOException e) { Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.getLocalizedMessage()); e.printStackTrace(); - Toast.makeText( - FileSelectActivity.this, - getText(R.string.error_file_not_create) + " " - + e.getLocalizedMessage(), - Toast.LENGTH_LONG).show(); - return false; - } - } + Toast.makeText( + FileSelectActivity.this, + getText(R.string.error_file_not_create) + " " + + e.getLocalizedMessage(), + Toast.LENGTH_LONG).show(); + return false; + } + } - @Override - public boolean onDefinePathDialogPositiveClick(Uri pathFile) { + @Override + public boolean onDefinePathDialogPositiveClick(Uri pathFile) { databaseUri = pathFile; if(createDatabaseFile(pathFile)) { AssignMasterKeyDialogFragment assignMasterKeyDialogFragment = new AssignMasterKeyDialogFragment(); @@ -516,97 +516,97 @@ public class FileSelectActivity extends StylishActivity implements return true; } else return false; - } + } - @Override - public boolean onDefinePathDialogNegativeClick(Uri pathFile) { + @Override + public boolean onDefinePathDialogNegativeClick(Uri pathFile) { return true; - } + } - @Override - public void onAssignKeyDialogPositiveClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { + @Override + public void onAssignKeyDialogPositiveClick( + boolean masterPasswordChecked, String masterPassword, + boolean keyFileChecked, Uri keyFile) { - try { - String databaseFilename = databaseUri.getPath(); + try { + String databaseFilename = databaseUri.getPath(); - if (databaseFilename != null) { - // Create the new database and start prof - new Thread(new ProgressDialogRunnable(this, - R.string.progress_create, - progressTaskUpdater -> - new CreateDatabaseRunnable(databaseFilename, database -> { - // TODO store database created - return new AssignPasswordInDatabaseRunnable(FileSelectActivity.this, - database, - masterPasswordChecked, - masterPassword, - keyFileChecked, - keyFile, - new LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename)), - true // TODO get readonly - ); - }) - )).start(); - } - } catch (Exception e) { - String error = "Unable to create database with this password and key file"; - Toast.makeText(this, error, Toast.LENGTH_LONG).show(); - Log.e(TAG, error + " " + e.getMessage()); - // TODO remove - e.printStackTrace(); - } - } + if (databaseFilename != null) { + // Create the new database and start prof + new Thread(new ProgressDialogRunnable(this, + R.string.progress_create, + progressTaskUpdater -> + new CreateDatabaseRunnable(databaseFilename, database -> { + // TODO store database created + return new AssignPasswordInDatabaseRunnable(FileSelectActivity.this, + database, + masterPasswordChecked, + masterPassword, + keyFileChecked, + keyFile, + new LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename)), + true // TODO get readonly + ); + }) + )).start(); + } + } catch (Exception e) { + String error = "Unable to create database with this password and key file"; + Toast.makeText(this, error, Toast.LENGTH_LONG).show(); + Log.e(TAG, error + " " + e.getMessage()); + // TODO remove + e.printStackTrace(); + } + } - private class LaunchGroupActivityFinish extends ActionRunnable { + private class LaunchGroupActivityFinish extends ActionRunnable { - private Uri fileURI; + private Uri fileURI; - LaunchGroupActivityFinish(Uri fileUri) { - super(); - this.fileURI = fileUri; - } + LaunchGroupActivityFinish(Uri fileUri) { + super(); + this.fileURI = fileUri; + } - @Override - public void run() { - finishRun(true, null); - } + @Override + public void run() { + finishRun(true, null); + } - @Override - public void onFinishRun(boolean isSuccess, @Nullable String message) { - runOnUiThread(() -> { - if (isSuccess) { - // Add database to recent files - fileHistory.createFile(fileURI); - mAdapter.notifyDataSetChanged(); - updateFileListVisibility(); - GroupActivity.launch(FileSelectActivity.this); - } else { - Log.e(TAG, "Unable to open the database"); - } - }); - } - } + @Override + public void onFinishRun(boolean isSuccess, @Nullable String message) { + runOnUiThread(() -> { + if (isSuccess) { + // Add database to recent files + fileHistory.createFile(fileURI); + mAdapter.notifyDataSetChanged(); + updateFileListVisibility(); + GroupActivity.launch(FileSelectActivity.this); + } else { + Log.e(TAG, "Unable to open the database"); + } + }); + } + } - @Override - public void onAssignKeyDialogNegativeClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { + @Override + public void onAssignKeyDialogNegativeClick( + boolean masterPasswordChecked, String masterPassword, + boolean keyFileChecked, Uri keyFile) { - } + } - @Override - public void onFileItemOpenListener(int itemPosition) { - new OpenFileHistoryAsyncTask((fileName, keyFile) -> { - launchPasswordActivity(fileName, keyFile); + @Override + public void onFileItemOpenListener(int itemPosition) { + new OpenFileHistoryAsyncTask((fileName, keyFile) -> { + launchPasswordActivity(fileName, keyFile); updateFileListVisibility(); }, fileHistory).execute(itemPosition); - } + } @Override public void onClickFileInformation(FileSelectBean fileSelectBean) { - if (fileSelectBean != null) { + if (fileSelectBean != null) { FileInformationDialogFragment fileInformationDialogFragment = FileInformationDialogFragment.newInstance(fileSelectBean); fileInformationDialogFragment.show(getSupportFragmentManager(), "fileInformation"); @@ -623,15 +623,15 @@ public class FileSelectActivity extends StylishActivity implements return true; } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); + } - keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, + keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, uri -> { if (uri != null) { if (PreferencesUtil.autoOpenSelectedFile(FileSelectActivity.this)) { @@ -642,7 +642,7 @@ public class FileSelectActivity extends StylishActivity implements } } }); - } + } @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) void showRationaleForExternalStorage(final PermissionRequest request) { @@ -663,16 +663,16 @@ public class FileSelectActivity extends StylishActivity implements Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show(); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuUtil.INSTANCE.defaultMenuInflater(getMenuInflater(), menu); - return true; - } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + MenuUtil.INSTANCE.defaultMenuInflater(getMenuInflater(), menu); + return true; + } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - return MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item) - && super.onOptionsItemSelected(item); - } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item) + && super.onOptionsItemSelected(item); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java index cffa1bd1f..f878e6494 100644 --- a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java +++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -48,21 +48,21 @@ public class IconDrawableFactory { private static final String TAG = IconDrawableFactory.class.getName(); - private static Drawable blank = null; - private static int blankWidth = -1; - private static int blankHeight = -1; - - /** customIconMap - * Cache for icon drawable. - * Keys: UUID, Values: Drawables - */ - private ReferenceMap customIconMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); - - /** standardIconMap - * Cache for icon drawable. - * Keys: Integer, Values: Drawables - */ - private ReferenceMap standardIconMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); + private static Drawable blank = null; + private static int blankWidth = -1; + private static int blankHeight = -1; + + /** customIconMap + * Cache for icon drawable. + * Keys: UUID, Values: Drawables + */ + private ReferenceMap customIconMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); + + /** standardIconMap + * Cache for icon drawable. + * Keys: Integer, Values: Drawables + */ + private ReferenceMap standardIconMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); /** * Assign a default database icon to an ImageView and tint it if needed @@ -95,19 +95,19 @@ public class IconDrawableFactory { * @param icon The icon from the database * @param tintColor Use this color to tint tintable icon */ - public void assignDatabaseIconTo(Context context, ImageView iconView, PwIcon icon, int tintColor) { - if (IconPackChooser.getSelectedIconPack(context).tintable()) { + public void assignDatabaseIconTo(Context context, ImageView iconView, PwIcon icon, int tintColor) { + if (IconPackChooser.getSelectedIconPack(context).tintable()) { assignDrawableToImageView(getIconDrawable(context, icon, true, tintColor), iconView, true, tintColor); - } else { + } else { assignDrawableToImageView(getIconDrawable(context, icon, true, tintColor), iconView, false, Color.WHITE); - } - } + } + } /** * Assign an image by its resourceId to an ImageView and tint it @@ -146,9 +146,9 @@ public class IconDrawableFactory { * @param icon The icon from database * @return The build drawable */ - public Drawable getIconDrawable(Context context, PwIcon icon) { - return getIconDrawable(context, icon, false, Color.WHITE).drawable; - } + public Drawable getIconDrawable(Context context, PwIcon icon) { + return getIconDrawable(context, icon, false, Color.WHITE).drawable; + } /** * Get the drawable icon from cache or build it and add it to the cache if not exists yet then tint it if needed @@ -171,27 +171,27 @@ public class IconDrawableFactory { * Build a blank drawable * @param res Resource to build the drawable */ - private static void initBlank(Resources res) { - if (blank==null) { - blankWidth = (int) res.getDimension(R.dimen.icon_size); - blankHeight = (int) res.getDimension(R.dimen.icon_size); - blank = new ColorDrawable(Color.TRANSPARENT); - blank.setBounds(0, 0, blankWidth, blankHeight); - } - } + private static void initBlank(Resources res) { + if (blank==null) { + blankWidth = (int) res.getDimension(R.dimen.icon_size); + blankHeight = (int) res.getDimension(R.dimen.icon_size); + blank = new ColorDrawable(Color.TRANSPARENT); + blank.setBounds(0, 0, blankWidth, blankHeight); + } + } /** * Key class to retrieve a Drawable in the cache if it's tinted or not */ - private class CacheKey { - int resId; - boolean isTint; - int color; + private class CacheKey { + int resId; + boolean isTint; + int color; - CacheKey(int resId, boolean isTint, int color) { - this.resId = resId; - this.isTint = isTint; - this.color = color; + CacheKey(int resId, boolean isTint, int color) { + this.resId = resId; + this.isTint = isTint; + this.color = color; } @Override @@ -202,7 +202,7 @@ public class IconDrawableFactory { if (isTint) return resId == cacheKey.resId && cacheKey.isTint && - color == cacheKey.color; + color == cacheKey.color; else return resId == cacheKey.resId && !cacheKey.isTint; @@ -218,11 +218,11 @@ public class IconDrawableFactory { * @param tintColor Use this color if tint is true * @return The drawable */ - private Drawable getIconDrawable(Context context, PwIconStandard icon, boolean isTint, int tintColor) { - int resId = IconPackChooser.getSelectedIconPack(context).iconToResId(icon.getIconId()); + private Drawable getIconDrawable(Context context, PwIconStandard icon, boolean isTint, int tintColor) { + int resId = IconPackChooser.getSelectedIconPack(context).iconToResId(icon.getIconId()); - return getIconDrawable(context, resId, isTint, tintColor); - } + return getIconDrawable(context, resId, isTint, tintColor); + } /** * Get the drawable icon from cache or build it and add it to the cache if not exists yet @@ -257,23 +257,23 @@ public class IconDrawableFactory { return draw; } - /** - * Utility class to prevent a custom icon to be tint - */ - private class SuperDrawable { - Drawable drawable; - boolean custom; + /** + * Utility class to prevent a custom icon to be tint + */ + private class SuperDrawable { + Drawable drawable; + boolean custom; - SuperDrawable(Drawable drawable) { - this.drawable = drawable; - this.custom = false; + SuperDrawable(Drawable drawable) { + this.drawable = drawable; + this.custom = false; } SuperDrawable(Drawable drawable, boolean custom) { this.drawable = drawable; this.custom = custom; } - } + } /** * Build a custom icon from database @@ -281,55 +281,55 @@ public class IconDrawableFactory { * @param icon Icon from database * @return The drawable */ - private Drawable getIconDrawable(Context context, PwIconCustom icon) { - initBlank(context.getResources()); - if (icon == null) { - return blank; - } - - Drawable draw = (Drawable) customIconMap.get(icon.getUuid()); - - if (draw == null) { + private Drawable getIconDrawable(Context context, PwIconCustom icon) { + initBlank(context.getResources()); + if (icon == null) { + return blank; + } - Bitmap bitmap = BitmapFactory.decodeByteArray(icon.getImageData(), 0, icon.getImageData().length); - - // Could not understand custom icon - if (bitmap == null) { - return blank; - } - - bitmap = resize(bitmap); - - draw = new BitmapDrawable(context.getResources(), bitmap); - customIconMap.put(icon.getUuid(), draw); - } + Drawable draw = (Drawable) customIconMap.get(icon.getUuid()); - return draw; - } - - /** + if (draw == null) { + + Bitmap bitmap = BitmapFactory.decodeByteArray(icon.getImageData(), 0, icon.getImageData().length); + + // Could not understand custom icon + if (bitmap == null) { + return blank; + } + + bitmap = resize(bitmap); + + draw = new BitmapDrawable(context.getResources(), bitmap); + customIconMap.put(icon.getUuid(), draw); + } + + return draw; + } + + /** * Resize the custom icon to match the built in icons * - * @param bitmap Bitmap to resize - * @return Bitmap resized - */ - private Bitmap resize(Bitmap bitmap) { - int width = bitmap.getWidth(); - int height = bitmap.getHeight(); - - if (width == blankWidth && height == blankHeight) { - return bitmap; - } - - return Bitmap.createScaledBitmap(bitmap, blankWidth, blankHeight, true); - } + * @param bitmap Bitmap to resize + * @return Bitmap resized + */ + private Bitmap resize(Bitmap bitmap) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + if (width == blankWidth && height == blankHeight) { + return bitmap; + } + + return Bitmap.createScaledBitmap(bitmap, blankWidth, blankHeight, true); + } /** * Clear the cache of icons */ - public void clearCache() { - standardIconMap.clear(); - customIconMap.clear(); - } - + public void clearCache() { + standardIconMap.clear(); + customIconMap.clear(); + } + } diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.java b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.java index b962f4b62..5b4ea4151 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.java +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/view/MagikeyboardView.java @@ -12,28 +12,28 @@ import static com.kunzisoft.keepass.magikeyboard.MagikIME.KEY_CHANGE_KEYBOARD; public class MagikeyboardView extends KeyboardView { - public MagikeyboardView(Context context, AttributeSet attrs) { - super(context, attrs); - } + public MagikeyboardView(Context context, AttributeSet attrs) { + super(context, attrs); + } - public MagikeyboardView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } + public MagikeyboardView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public MagikeyboardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public MagikeyboardView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } - @Override - protected boolean onLongPress(Keyboard.Key key) { - // TODO Action on long press - if (key.codes[0] == KEY_BACK_KEYBOARD) { - getOnKeyboardActionListener().onKey(KEY_CHANGE_KEYBOARD, null); - return true; - } else { - //Log.d("LatinKeyboardView", "KEY: " + key.codes[0]); - return super.onLongPress(key); - } - } + @Override + protected boolean onLongPress(Keyboard.Key key) { + // TODO Action on long press + if (key.codes[0] == KEY_BACK_KEYBOARD) { + getOnKeyboardActionListener().onKey(KEY_CHANGE_KEYBOARD, null); + return true; + } else { + //Log.d("LatinKeyboardView", "KEY: " + key.codes[0]); + return super.onLongPress(key); + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.java index a59482314..7a9f352ff 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordGenerator.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -26,14 +26,14 @@ import com.kunzisoft.keepass.R; import java.security.SecureRandom; public class PasswordGenerator { - private static final String UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - private static final String LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz"; - private static final String DIGIT_CHARS = "0123456789"; - private static final String MINUS_CHAR = "-"; - private static final String UNDERLINE_CHAR = "_"; - private static final String SPACE_CHAR = " "; - private static final String SPECIAL_CHARS = "!\"#$%&'*+,./:;=?@\\^`"; - private static final String BRACKET_CHARS = "[]{}()<>"; + private static final String UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz"; + private static final String DIGIT_CHARS = "0123456789"; + private static final String MINUS_CHAR = "-"; + private static final String UNDERLINE_CHAR = "_"; + private static final String SPACE_CHAR = " "; + private static final String SPECIAL_CHARS = "!\"#$%&'*+,./:;=?@\\^`"; + private static final String BRACKET_CHARS = "[]{}()<>"; // From KeePassXC code https://github.com/keepassxreboot/keepassxc/pull/538 private String extendedChars() { @@ -48,14 +48,14 @@ public class PasswordGenerator { charSet.append('\u00FF'); return charSet.toString(); } - - private Context cxt; - - public PasswordGenerator(Context cxt) { - this.cxt = cxt; - } - - public String generatePassword(int length, + + private Context cxt; + + public PasswordGenerator(Context cxt) { + this.cxt = cxt; + } + + public String generatePassword(int length, boolean upperCase, boolean lowerCase, boolean digits, @@ -65,13 +65,13 @@ public class PasswordGenerator { boolean specials, boolean brackets, boolean extended) throws IllegalArgumentException{ - // Desired password length is 0 or less - if (length <= 0) { - throw new IllegalArgumentException(cxt.getString(R.string.error_wrong_length)); - } - - // No option has been checked - if ( !upperCase + // Desired password length is 0 or less + if (length <= 0) { + throw new IllegalArgumentException(cxt.getString(R.string.error_wrong_length)); + } + + // No option has been checked + if ( !upperCase && !lowerCase && !digits && !minus @@ -80,11 +80,11 @@ public class PasswordGenerator { && !specials && !brackets && !extended) { - throw new IllegalArgumentException(cxt.getString(R.string.error_pass_gen_type)); - } - - String characterSet = getCharacterSet( - upperCase, + throw new IllegalArgumentException(cxt.getString(R.string.error_pass_gen_type)); + } + + String characterSet = getCharacterSet( + upperCase, lowerCase, digits, minus, @@ -93,68 +93,68 @@ public class PasswordGenerator { specials, brackets, extended); - - int size = characterSet.length(); - - StringBuilder buffer = new StringBuilder(); - SecureRandom random = new SecureRandom(); // use more secure variant of Random! - if (size > 0) { - for (int i = 0; i < length; i++) { - char c = characterSet.charAt((char) random.nextInt(size)); - buffer.append(c); - } - } - return buffer.toString(); - } - - private String getCharacterSet(boolean upperCase, - boolean lowerCase, - boolean digits, - boolean minus, - boolean underline, - boolean space, - boolean specials, - boolean brackets, - boolean extended) { - StringBuilder charSet = new StringBuilder(); - - if (upperCase) { - charSet.append(UPPERCASE_CHARS); - } - - if (lowerCase) { - charSet.append(LOWERCASE_CHARS); - } - - if (digits) { - charSet.append(DIGIT_CHARS); - } - - if (minus) { - charSet.append(MINUS_CHAR); - } - - if (underline) { - charSet.append(UNDERLINE_CHAR); - } - - if (space) { - charSet.append(SPACE_CHAR); - } - - if (specials) { - charSet.append(SPECIAL_CHARS); - } - - if (brackets) { - charSet.append(BRACKET_CHARS); - } + int size = characterSet.length(); - if (extended) { + StringBuilder buffer = new StringBuilder(); + + SecureRandom random = new SecureRandom(); // use more secure variant of Random! + if (size > 0) { + for (int i = 0; i < length; i++) { + char c = characterSet.charAt((char) random.nextInt(size)); + buffer.append(c); + } + } + return buffer.toString(); + } + + private String getCharacterSet(boolean upperCase, + boolean lowerCase, + boolean digits, + boolean minus, + boolean underline, + boolean space, + boolean specials, + boolean brackets, + boolean extended) { + StringBuilder charSet = new StringBuilder(); + + if (upperCase) { + charSet.append(UPPERCASE_CHARS); + } + + if (lowerCase) { + charSet.append(LOWERCASE_CHARS); + } + + if (digits) { + charSet.append(DIGIT_CHARS); + } + + if (minus) { + charSet.append(MINUS_CHAR); + } + + if (underline) { + charSet.append(UNDERLINE_CHAR); + } + + if (space) { + charSet.append(SPACE_CHAR); + } + + if (specials) { + charSet.append(SPECIAL_CHARS); + } + + if (brackets) { + charSet.append(BRACKET_CHARS); + } + + if (extended) { charSet.append(extendedChars()); } - return charSet.toString(); - } + return charSet.toString(); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/CopyInputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/CopyInputStream.java index 52fe74eea..e9d9c73c9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/CopyInputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/CopyInputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -28,76 +28,76 @@ import java.io.OutputStream; * output stream. */ public class CopyInputStream extends InputStream { - private InputStream is; - private OutputStream os; - - public CopyInputStream(InputStream is, OutputStream os) { - this.is = is; - this.os = os; - } + private InputStream is; + private OutputStream os; - @Override - public int available() throws IOException { - return is.available(); - } + public CopyInputStream(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } - @Override - public void close() throws IOException { - is.close(); - os.close(); - } + @Override + public int available() throws IOException { + return is.available(); + } - @Override - public void mark(int readlimit) { - is.mark(readlimit); - } + @Override + public void close() throws IOException { + is.close(); + os.close(); + } - @Override - public boolean markSupported() { - return is.markSupported(); - } + @Override + public void mark(int readlimit) { + is.mark(readlimit); + } - @Override - public int read() throws IOException { - int data = is.read(); - - if (data != -1) { - os.write(data); - } - - return data; - } + @Override + public boolean markSupported() { + return is.markSupported(); + } - @Override - public int read(byte[] b, int offset, int length) throws IOException { - int len = is.read(b, offset, length); - - if (len != -1) { - os.write(b, offset, len); - } - - return len; - } + @Override + public int read() throws IOException { + int data = is.read(); - @Override - public int read(byte[] b) throws IOException { - int len = is.read(b); - - if (len != -1) { - os.write(b, 0, len); - } - - return len; - } + if (data != -1) { + os.write(data); + } - @Override - public synchronized void reset() throws IOException { - is.reset(); - } + return data; + } - @Override - public long skip(long byteCount) throws IOException { - return is.skip(byteCount); - } + @Override + public int read(byte[] b, int offset, int length) throws IOException { + int len = is.read(b, offset, length); + + if (len != -1) { + os.write(b, offset, len); + } + + return len; + } + + @Override + public int read(byte[] b) throws IOException { + int len = is.read(b); + + if (len != -1) { + os.write(b, 0, len); + } + + return len; + } + + @Override + public synchronized void reset() throws IOException { + is.reset(); + } + + @Override + public long skip(long byteCount) throws IOException { + return is.skip(byteCount); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/CountInputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/CountInputStream.java index 604a93b19..de507a964 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/CountInputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/CountInputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -23,60 +23,60 @@ import java.io.IOException; import java.io.InputStream; public class CountInputStream extends InputStream { - InputStream is; - long bytes = 0; - - public CountInputStream(InputStream is) { - this.is = is; - } + InputStream is; + long bytes = 0; - @Override - public int available() throws IOException { - return is.available(); - } + public CountInputStream(InputStream is) { + this.is = is; + } - @Override - public void close() throws IOException { - is.close(); - } + @Override + public int available() throws IOException { + return is.available(); + } - @Override - public void mark(int readlimit) { - is.mark(readlimit); - } + @Override + public void close() throws IOException { + is.close(); + } - @Override - public boolean markSupported() { - return is.markSupported(); - } + @Override + public void mark(int readlimit) { + is.mark(readlimit); + } - @Override - public int read() throws IOException { - bytes++; - return is.read(); - } + @Override + public boolean markSupported() { + return is.markSupported(); + } - @Override - public int read(byte[] buffer, int offset, int length) throws IOException { - bytes += length; - return is.read(buffer, offset, length); - } + @Override + public int read() throws IOException { + bytes++; + return is.read(); + } - @Override - public int read(byte[] buffer) throws IOException { - bytes += buffer.length; - return is.read(buffer); - } + @Override + public int read(byte[] buffer, int offset, int length) throws IOException { + bytes += length; + return is.read(buffer, offset, length); + } - @Override - public synchronized void reset() throws IOException { - is.reset(); - } + @Override + public int read(byte[] buffer) throws IOException { + bytes += buffer.length; + return is.read(buffer); + } - @Override - public long skip(long byteCount) throws IOException { - bytes += byteCount; - return is.skip(byteCount); - } + @Override + public synchronized void reset() throws IOException { + is.reset(); + } + + @Override + public long skip(long byteCount) throws IOException { + bytes += byteCount; + return is.skip(byteCount); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/CountOutputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/CountOutputStream.java index 945874a1c..0ca655b05 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/CountOutputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/CountOutputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -23,40 +23,40 @@ import java.io.IOException; import java.io.OutputStream; public class CountOutputStream extends OutputStream { - OutputStream os; - long bytes = 0; - - public CountOutputStream(OutputStream os) { - this.os = os; - } + OutputStream os; + long bytes = 0; + + public CountOutputStream(OutputStream os) { + this.os = os; + } - @Override - public void close() throws IOException { - os.close(); - } + @Override + public void close() throws IOException { + os.close(); + } - @Override - public void flush() throws IOException { - os.flush(); - } + @Override + public void flush() throws IOException { + os.flush(); + } - @Override - public void write(byte[] buffer, int offset, int count) throws IOException { - bytes += count; - os.write(buffer, offset, count); - } + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + bytes += count; + os.write(buffer, offset, count); + } - @Override - public void write(byte[] buffer) throws IOException { - bytes += buffer.length; - os.write(buffer); - } + @Override + public void write(byte[] buffer) throws IOException { + bytes += buffer.length; + os.write(buffer); + } - @Override - public void write(int oneByte) throws IOException { - bytes++; - os.write(oneByte); - } + @Override + public void write(int oneByte) throws IOException { + bytes++; + os.write(oneByte); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.java index c44a2b6bd..9adafcbfc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockInputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -29,137 +29,137 @@ import java.util.Arrays; public class HashedBlockInputStream extends InputStream { - - private final static int HASH_SIZE = 32; - private LEDataInputStream baseStream; - private int bufferPos = 0; - private byte[] buffer = new byte[0]; - private long bufferIndex = 0; - private boolean atEnd = false; - - - @Override - public int read(byte[] b) throws IOException { - return read(b, 0, b.length); - } + private final static int HASH_SIZE = 32; - public HashedBlockInputStream(InputStream is) { - baseStream = new LEDataInputStream(is); - } - - @Override - public int read(byte[] b, int offset, int length) throws IOException { - if ( atEnd ) return -1; - - int remaining = length; - - while ( remaining > 0 ) { - if ( bufferPos == buffer.length ) { - // Get more from the source into the buffer - if ( ! ReadHashedBlock() ) { - return length - remaining; - } - - } + private LEDataInputStream baseStream; + private int bufferPos = 0; + private byte[] buffer = new byte[0]; + private long bufferIndex = 0; + private boolean atEnd = false; - // Copy from buffer out - int copyLen = Math.min(buffer.length - bufferPos, remaining); - - System.arraycopy(buffer, bufferPos, b, offset, copyLen); - - offset += copyLen; - bufferPos += copyLen; - - remaining -= copyLen; - } - - return length; - } - /** - * @return false, when the end of the source stream is reached - * @throws IOException - */ - private boolean ReadHashedBlock() throws IOException { - if ( atEnd ) return false; - - bufferPos = 0; - - long index = baseStream.readUInt(); - if ( index != bufferIndex ) { - throw new IOException("Invalid data format"); - } - bufferIndex++; - - byte[] storedHash = baseStream.readBytes(32); - if ( storedHash == null || storedHash.length != HASH_SIZE) { - throw new IOException("Invalid data format"); - } - - int bufferSize = LEDataInputStream.readInt(baseStream); - if ( bufferSize < 0 ) { - throw new IOException("Invalid data format"); - } - - if ( bufferSize == 0 ) { - for (int hash = 0; hash < HASH_SIZE; hash++) { - if ( storedHash[hash] != 0 ) { - throw new IOException("Invalid data format"); - } - } - - atEnd = true; - buffer = new byte[0]; - return false; - } - - buffer = baseStream.readBytes(bufferSize); - if ( buffer == null || buffer.length != bufferSize ) { - throw new IOException("Invalid data format"); - } - - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IOException("SHA-256 not implemented here."); - } - - byte[] computedHash = md.digest(buffer); - if ( computedHash == null || computedHash.length != HASH_SIZE ) { - throw new IOException("Hash wrong size"); - } - - if ( ! Arrays.equals(storedHash, computedHash) ) { - throw new IOException("Hashes didn't match."); - } + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } - return true; - } + public HashedBlockInputStream(InputStream is) { + baseStream = new LEDataInputStream(is); + } - @Override - public long skip(long n) throws IOException { - return 0; - } + @Override + public int read(byte[] b, int offset, int length) throws IOException { + if ( atEnd ) return -1; - @Override - public int read() throws IOException { - if ( atEnd ) return -1; - - if ( bufferPos == buffer.length ) { - if ( ! ReadHashedBlock() ) return -1; - } - - int output = Types.readUByte(buffer, bufferPos); - bufferPos++; - - return output; - } + int remaining = length; - @Override - public void close() throws IOException { - baseStream.close(); - } + while ( remaining > 0 ) { + if ( bufferPos == buffer.length ) { + // Get more from the source into the buffer + if ( ! ReadHashedBlock() ) { + return length - remaining; + } + + } + + // Copy from buffer out + int copyLen = Math.min(buffer.length - bufferPos, remaining); + + System.arraycopy(buffer, bufferPos, b, offset, copyLen); + + offset += copyLen; + bufferPos += copyLen; + + remaining -= copyLen; + } + + return length; + } + + /** + * @return false, when the end of the source stream is reached + * @throws IOException + */ + private boolean ReadHashedBlock() throws IOException { + if ( atEnd ) return false; + + bufferPos = 0; + + long index = baseStream.readUInt(); + if ( index != bufferIndex ) { + throw new IOException("Invalid data format"); + } + bufferIndex++; + + byte[] storedHash = baseStream.readBytes(32); + if ( storedHash == null || storedHash.length != HASH_SIZE) { + throw new IOException("Invalid data format"); + } + + int bufferSize = LEDataInputStream.readInt(baseStream); + if ( bufferSize < 0 ) { + throw new IOException("Invalid data format"); + } + + if ( bufferSize == 0 ) { + for (int hash = 0; hash < HASH_SIZE; hash++) { + if ( storedHash[hash] != 0 ) { + throw new IOException("Invalid data format"); + } + } + + atEnd = true; + buffer = new byte[0]; + return false; + } + + buffer = baseStream.readBytes(bufferSize); + if ( buffer == null || buffer.length != bufferSize ) { + throw new IOException("Invalid data format"); + } + + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not implemented here."); + } + + byte[] computedHash = md.digest(buffer); + if ( computedHash == null || computedHash.length != HASH_SIZE ) { + throw new IOException("Hash wrong size"); + } + + if ( ! Arrays.equals(storedHash, computedHash) ) { + throw new IOException("Hashes didn't match."); + } + + return true; + } + + @Override + public long skip(long n) throws IOException { + return 0; + } + + @Override + public int read() throws IOException { + if ( atEnd ) return -1; + + if ( bufferPos == buffer.length ) { + if ( ! ReadHashedBlock() ) return -1; + } + + int output = Types.readUByte(buffer, bufferPos); + bufferPos++; + + return output; + } + + @Override + public void close() throws IOException { + baseStream.close(); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.java index b1fce5c47..ab88a4a5d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/HashedBlockOutputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -26,90 +26,90 @@ import java.security.NoSuchAlgorithmException; public class HashedBlockOutputStream extends OutputStream { - private final static int DEFAULT_BUFFER_SIZE = 1024 * 1024; - - private LEDataOutputStream baseStream; - private int bufferPos = 0; - private byte[] buffer; - private long bufferIndex = 0; - - public HashedBlockOutputStream(OutputStream os) { - init(os, DEFAULT_BUFFER_SIZE); - } - - public HashedBlockOutputStream(OutputStream os, int bufferSize) { - if ( bufferSize <= 0 ) { - bufferSize = DEFAULT_BUFFER_SIZE; - } - - init(os, bufferSize); - } - - private void init(OutputStream os, int bufferSize) { - baseStream = new LEDataOutputStream(os); - buffer = new byte[bufferSize]; - - } + private final static int DEFAULT_BUFFER_SIZE = 1024 * 1024; - @Override - public void write(int oneByte) throws IOException { - byte[] buf = new byte[1]; - buf[0] = (byte)oneByte; - write(buf, 0, 1); - } + private LEDataOutputStream baseStream; + private int bufferPos = 0; + private byte[] buffer; + private long bufferIndex = 0; - @Override - public void close() throws IOException { - if ( bufferPos != 0 ) { - // Write remaining buffered amount - WriteHashedBlock(); - } - - // Write terminating block - WriteHashedBlock(); - - flush(); - baseStream.close(); - } + public HashedBlockOutputStream(OutputStream os) { + init(os, DEFAULT_BUFFER_SIZE); + } - @Override - public void flush() throws IOException { - baseStream.flush(); - } + public HashedBlockOutputStream(OutputStream os, int bufferSize) { + if ( bufferSize <= 0 ) { + bufferSize = DEFAULT_BUFFER_SIZE; + } - @Override - public void write(byte[] b, int offset, int count) throws IOException { - while ( count > 0 ) { - if ( bufferPos == buffer.length ) { - WriteHashedBlock(); - } - - int copyLen = Math.min(buffer.length - bufferPos, count); - - System.arraycopy(b, offset, buffer, bufferPos, copyLen); - - offset += copyLen; - bufferPos += copyLen; - - count -= copyLen; - } - } + init(os, bufferSize); + } - private void WriteHashedBlock() throws IOException { - baseStream.writeUInt(bufferIndex); - bufferIndex++; - - if ( bufferPos > 0 ) { - MessageDigest md = null; - try { - md = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IOException("SHA-256 not implemented here."); - } - - byte[] hash; - md.update(buffer, 0, bufferPos); - hash = md.digest(); + private void init(OutputStream os, int bufferSize) { + baseStream = new LEDataOutputStream(os); + buffer = new byte[bufferSize]; + + } + + @Override + public void write(int oneByte) throws IOException { + byte[] buf = new byte[1]; + buf[0] = (byte)oneByte; + write(buf, 0, 1); + } + + @Override + public void close() throws IOException { + if ( bufferPos != 0 ) { + // Write remaining buffered amount + WriteHashedBlock(); + } + + // Write terminating block + WriteHashedBlock(); + + flush(); + baseStream.close(); + } + + @Override + public void flush() throws IOException { + baseStream.flush(); + } + + @Override + public void write(byte[] b, int offset, int count) throws IOException { + while ( count > 0 ) { + if ( bufferPos == buffer.length ) { + WriteHashedBlock(); + } + + int copyLen = Math.min(buffer.length - bufferPos, count); + + System.arraycopy(b, offset, buffer, bufferPos, copyLen); + + offset += copyLen; + bufferPos += copyLen; + + count -= copyLen; + } + } + + private void WriteHashedBlock() throws IOException { + baseStream.writeUInt(bufferIndex); + bufferIndex++; + + if ( bufferPos > 0 ) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not implemented here."); + } + + byte[] hash; + md.update(buffer, 0, bufferPos); + hash = md.digest(); /* if ( bufferPos == buffer.length) { hash = md.digest(buffer); @@ -119,30 +119,30 @@ public class HashedBlockOutputStream extends OutputStream { hash = md.digest(b); } */ - - baseStream.write(hash); - } else { - // Write 32-bits of zeros - baseStream.writeLong(0L); - baseStream.writeLong(0L); - baseStream.writeLong(0L); - baseStream.writeLong(0L); - } - - baseStream.writeInt(bufferPos); - - if ( bufferPos > 0 ) { - baseStream.write(buffer, 0, bufferPos); - } - - bufferPos = 0; - - } + baseStream.write(hash); - @Override - public void write(byte[] buffer) throws IOException { - write(buffer, 0, buffer.length); - } + } else { + // Write 32-bits of zeros + baseStream.writeLong(0L); + baseStream.writeLong(0L); + baseStream.writeLong(0L); + baseStream.writeLong(0L); + } + + baseStream.writeInt(bufferPos); + + if ( bufferPos > 0 ) { + baseStream.write(buffer, 0, bufferPos); + } + + bufferPos = 0; + + } + + @Override + public void write(byte[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/LEDataInputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/LEDataInputStream.java index 1af163754..fb3ef1615 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/LEDataInputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/LEDataInputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -30,100 +30,100 @@ import java.util.Arrays; */ public class LEDataInputStream extends InputStream { - private static final long INT_TO_LONG_MASK = 0xffffffffL; - - private InputStream baseStream; + private static final long INT_TO_LONG_MASK = 0xffffffffL; - public LEDataInputStream(InputStream in) { - baseStream = in; - } - - /** Read a 32-bit value and return it as a long, so that it can - * be interpreted as an unsigned integer. - * @return - * @throws IOException - */ - public long readUInt() throws IOException { - return readUInt(baseStream); - } - - public int readInt() throws IOException { - return readInt(baseStream); - } - - public long readLong() throws IOException { - byte[] buf = readBytes(8); - - return readLong(buf, 0); - } - - @Override - public int available() throws IOException { - return baseStream.available(); - } + private InputStream baseStream; - @Override - public void close() throws IOException { - baseStream.close(); - } + public LEDataInputStream(InputStream in) { + baseStream = in; + } - @Override - public void mark(int readlimit) { - baseStream.mark(readlimit); - } + /** Read a 32-bit value and return it as a long, so that it can + * be interpreted as an unsigned integer. + * @return + * @throws IOException + */ + public long readUInt() throws IOException { + return readUInt(baseStream); + } - @Override - public boolean markSupported() { - return baseStream.markSupported(); - } + public int readInt() throws IOException { + return readInt(baseStream); + } - @Override - public int read() throws IOException { - return baseStream.read(); - } + public long readLong() throws IOException { + byte[] buf = readBytes(8); - @Override - public int read(byte[] b, int offset, int length) throws IOException { - return baseStream.read(b, offset, length); - } + return readLong(buf, 0); + } - @Override - public int read(byte[] b) throws IOException { - // TODO Auto-generated method stub - return super.read(b); - } + @Override + public int available() throws IOException { + return baseStream.available(); + } - @Override - public synchronized void reset() throws IOException { - baseStream.reset(); - } + @Override + public void close() throws IOException { + baseStream.close(); + } - @Override - public long skip(long n) throws IOException { - return baseStream.skip(n); - } + @Override + public void mark(int readlimit) { + baseStream.mark(readlimit); + } - public byte[] readBytes(int length) throws IOException { - // TODO Exception max length < buffer size - byte[] buf = new byte[length]; + @Override + public boolean markSupported() { + return baseStream.markSupported(); + } - int count = 0; - while ( count < length ) { - int read = read(buf, count, length - count); - - // Reached end - if ( read == -1 ) { - // Stop early - byte[] early = new byte[count]; - System.arraycopy(buf, 0, early, 0, count); - return early; - } - - count += read; - } - - return buf; - } + @Override + public int read() throws IOException { + return baseStream.read(); + } + + @Override + public int read(byte[] b, int offset, int length) throws IOException { + return baseStream.read(b, offset, length); + } + + @Override + public int read(byte[] b) throws IOException { + // TODO Auto-generated method stub + return super.read(b); + } + + @Override + public synchronized void reset() throws IOException { + baseStream.reset(); + } + + @Override + public long skip(long n) throws IOException { + return baseStream.skip(n); + } + + public byte[] readBytes(int length) throws IOException { + // TODO Exception max length < buffer size + byte[] buf = new byte[length]; + + int count = 0; + while ( count < length ) { + int read = read(buf, count, length - count); + + // Reached end + if ( read == -1 ) { + // Stop early + byte[] early = new byte[count]; + System.arraycopy(buf, 0, early, 0, count); + return early; + } + + count += read; + } + + return buf; + } public void readBytes(int length, ActionReadBytes actionReadBytes) throws IOException { int bufferSize = 256 * 3; // TODO Buffer size @@ -152,62 +152,62 @@ public class LEDataInputStream extends InputStream { } } - public static int readUShort(InputStream is) throws IOException { - byte[] buf = new byte[2]; - - is.read(buf, 0, 2); - - return readUShort(buf, 0); - } - - public int readUShort() throws IOException { - return readUShort(baseStream); - } + public static int readUShort(InputStream is) throws IOException { + byte[] buf = new byte[2]; - /** - * Read an unsigned 16-bit value. - * - * @param buf - * @param offset - * @return - */ - public static int readUShort( byte[] buf, int offset ) { - return (buf[offset] & 0xFF) + ((buf[offset + 1] & 0xFF) << 8); - } + is.read(buf, 0, 2); - public static long readLong( byte buf[], int offset ) { - return ((long)buf[offset] & 0xFF) + (((long)buf[offset + 1] & 0xFF) << 8) - + (((long)buf[offset + 2] & 0xFF) << 16) + (((long)buf[offset + 3] & 0xFF) << 24) - + (((long)buf[offset + 4] & 0xFF) << 32) + (((long)buf[offset + 5] & 0xFF) << 40) - + (((long)buf[offset + 6] & 0xFF) << 48) + (((long)buf[offset + 7] & 0xFF) << 56); - } + return readUShort(buf, 0); + } - public static long readUInt( byte buf[], int offset ) { - return (readInt(buf, offset) & INT_TO_LONG_MASK); - } + public int readUShort() throws IOException { + return readUShort(baseStream); + } - public static int readInt(InputStream is) throws IOException { - byte[] buf = new byte[4]; - - is.read(buf, 0, 4); - - return readInt(buf, 0); - } + /** + * Read an unsigned 16-bit value. + * + * @param buf + * @param offset + * @return + */ + public static int readUShort( byte[] buf, int offset ) { + return (buf[offset] & 0xFF) + ((buf[offset + 1] & 0xFF) << 8); + } - public static long readUInt(InputStream is) throws IOException { - return (readInt(is) & INT_TO_LONG_MASK); - } + public static long readLong( byte buf[], int offset ) { + return ((long)buf[offset] & 0xFF) + (((long)buf[offset + 1] & 0xFF) << 8) + + (((long)buf[offset + 2] & 0xFF) << 16) + (((long)buf[offset + 3] & 0xFF) << 24) + + (((long)buf[offset + 4] & 0xFF) << 32) + (((long)buf[offset + 5] & 0xFF) << 40) + + (((long)buf[offset + 6] & 0xFF) << 48) + (((long)buf[offset + 7] & 0xFF) << 56); + } - /** - * Read a 32-bit value. - * - * @param buf - * @param offset - * @return - */ - public static int readInt( byte buf[], int offset ) { - return (buf[offset] & 0xFF) + ((buf[offset + 1] & 0xFF) << 8) + ((buf[offset + 2] & 0xFF) << 16) - + ((buf[offset + 3] & 0xFF) << 24); - } + public static long readUInt( byte buf[], int offset ) { + return (readInt(buf, offset) & INT_TO_LONG_MASK); + } + + public static int readInt(InputStream is) throws IOException { + byte[] buf = new byte[4]; + + is.read(buf, 0, 4); + + return readInt(buf, 0); + } + + public static long readUInt(InputStream is) throws IOException { + return (readInt(is) & INT_TO_LONG_MASK); + } + + /** + * Read a 32-bit value. + * + * @param buf + * @param offset + * @return + */ + public static int readInt( byte buf[], int offset ) { + return (buf[offset] & 0xFF) + ((buf[offset + 1] & 0xFF) << 8) + ((buf[offset + 2] & 0xFF) << 16) + + ((buf[offset + 3] & 0xFF) << 24); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/LEDataOutputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/LEDataOutputStream.java index 82a02fbe6..bb4cf26eb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/LEDataOutputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/LEDataOutputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -29,116 +29,116 @@ import java.io.OutputStream; */ public class LEDataOutputStream extends OutputStream { - private OutputStream baseStream; - - public LEDataOutputStream(OutputStream out) { - baseStream = out; - } - - public void writeUInt(long uint) throws IOException { - baseStream.write(LEDataOutputStream.writeIntBuf((int) uint)); - } + private OutputStream baseStream; - @Override - public void close() throws IOException { - baseStream.close(); - } + public LEDataOutputStream(OutputStream out) { + baseStream = out; + } - @Override - public void flush() throws IOException { - baseStream.flush(); - } + public void writeUInt(long uint) throws IOException { + baseStream.write(LEDataOutputStream.writeIntBuf((int) uint)); + } - @Override - public void write(byte[] buffer, int offset, int count) throws IOException { - baseStream.write(buffer, offset, count); - } + @Override + public void close() throws IOException { + baseStream.close(); + } - @Override - public void write(byte[] buffer) throws IOException { - baseStream.write(buffer); - } + @Override + public void flush() throws IOException { + baseStream.flush(); + } - @Override - public void write(int oneByte) throws IOException { - baseStream.write(oneByte); - } - - public void writeLong(long val) throws IOException { - byte[] buf = new byte[8]; - - writeLong(val, buf, 0); - baseStream.write(buf); - } - - public void writeInt(int val) throws IOException { - byte[] buf = new byte[4]; - writeInt(val, buf, 0); - - baseStream.write(buf); - } - - public void writeUShort(int val) throws IOException { - byte[] buf = new byte[2]; - writeUShort(val, buf, 0); - baseStream.write(buf); - } + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + baseStream.write(buffer, offset, count); + } - public static byte[] writeIntBuf(int val) { - byte[] buf = new byte[4]; - writeInt(val, buf, 0); - - return buf; - } + @Override + public void write(byte[] buffer) throws IOException { + baseStream.write(buffer); + } - public static byte[] writeUShortBuf(int val) { - byte[] buf = new byte[2]; - - writeUShort(val, buf, 0); - - return buf; - } + @Override + public void write(int oneByte) throws IOException { + baseStream.write(oneByte); + } - /** Write an unsigned 16-bit value - * - * @param val - * @param buf - * @param offset - */ - public static void writeUShort(int val, byte[] buf, int offset) { - buf[offset + 0] = (byte)(val & 0x00FF); - buf[offset + 1] = (byte)((val & 0xFF00) >> 8); - } + public void writeLong(long val) throws IOException { + byte[] buf = new byte[8]; - /** - * Write a 32-bit value. - * - * @param val - * @param buf - * @param offset - */ - public static void writeInt( int val, byte[] buf, int offset ) { - buf[offset + 0] = (byte)(val & 0xFF); - buf[offset + 1] = (byte)((val >>> 8) & 0xFF); - buf[offset + 2] = (byte)((val >>> 16) & 0xFF); - buf[offset + 3] = (byte)((val >>> 24) & 0xFF); - } - - public static byte[] writeLongBuf(long val) { - byte[] buf = new byte[8]; - writeLong(val, buf, 0); - return buf; - } + writeLong(val, buf, 0); + baseStream.write(buf); + } - public static void writeLong( long val, byte[] buf, int offset ) { - buf[offset + 0] = (byte)(val & 0xFF); - buf[offset + 1] = (byte)((val >>> 8) & 0xFF); - buf[offset + 2] = (byte)((val >>> 16) & 0xFF); - buf[offset + 3] = (byte)((val >>> 24) & 0xFF); - buf[offset + 4] = (byte)((val >>> 32) & 0xFF); - buf[offset + 5] = (byte)((val >>> 40) & 0xFF); - buf[offset + 6] = (byte)((val >>> 48) & 0xFF); - buf[offset + 7] = (byte)((val >>> 56) & 0xFF); - } + public void writeInt(int val) throws IOException { + byte[] buf = new byte[4]; + writeInt(val, buf, 0); + + baseStream.write(buf); + } + + public void writeUShort(int val) throws IOException { + byte[] buf = new byte[2]; + writeUShort(val, buf, 0); + baseStream.write(buf); + } + + public static byte[] writeIntBuf(int val) { + byte[] buf = new byte[4]; + writeInt(val, buf, 0); + + return buf; + } + + public static byte[] writeUShortBuf(int val) { + byte[] buf = new byte[2]; + + writeUShort(val, buf, 0); + + return buf; + } + + /** Write an unsigned 16-bit value + * + * @param val + * @param buf + * @param offset + */ + public static void writeUShort(int val, byte[] buf, int offset) { + buf[offset + 0] = (byte)(val & 0x00FF); + buf[offset + 1] = (byte)((val & 0xFF00) >> 8); + } + + /** + * Write a 32-bit value. + * + * @param val + * @param buf + * @param offset + */ + public static void writeInt( int val, byte[] buf, int offset ) { + buf[offset + 0] = (byte)(val & 0xFF); + buf[offset + 1] = (byte)((val >>> 8) & 0xFF); + buf[offset + 2] = (byte)((val >>> 16) & 0xFF); + buf[offset + 3] = (byte)((val >>> 24) & 0xFF); + } + + public static byte[] writeLongBuf(long val) { + byte[] buf = new byte[8]; + writeLong(val, buf, 0); + return buf; + } + + public static void writeLong( long val, byte[] buf, int offset ) { + buf[offset + 0] = (byte)(val & 0xFF); + buf[offset + 1] = (byte)((val >>> 8) & 0xFF); + buf[offset + 2] = (byte)((val >>> 16) & 0xFF); + buf[offset + 3] = (byte)((val >>> 24) & 0xFF); + buf[offset + 4] = (byte)((val >>> 32) & 0xFF); + buf[offset + 5] = (byte)((val >>> 40) & 0xFF); + buf[offset + 6] = (byte)((val >>> 48) & 0xFF); + buf[offset + 7] = (byte)((val >>> 56) & 0xFF); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/NullOutputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/NullOutputStream.java index 0e1a70b66..12eef13e3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/NullOutputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/NullOutputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -24,28 +24,28 @@ import java.io.OutputStream; public class NullOutputStream extends OutputStream { - @Override - public void close() throws IOException { - super.close(); - } + @Override + public void close() throws IOException { + super.close(); + } - @Override - public void flush() throws IOException { - super.flush(); - } + @Override + public void flush() throws IOException { + super.flush(); + } - @Override - public void write(byte[] buffer, int offset, int count) throws IOException { - super.write(buffer, offset, count); - } + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + super.write(buffer, offset, count); + } - @Override - public void write(byte[] buffer) throws IOException { - super.write(buffer); - } + @Override + public void write(byte[] buffer) throws IOException { + super.write(buffer); + } - @Override - public void write(int oneByte) throws IOException { - } + @Override + public void write(int oneByte) throws IOException { + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/stream/RandomFileOutputStream.java b/app/src/main/java/com/kunzisoft/keepass/stream/RandomFileOutputStream.java index 220ba2e64..06ce4f594 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stream/RandomFileOutputStream.java +++ b/app/src/main/java/com/kunzisoft/keepass/stream/RandomFileOutputStream.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -25,40 +25,40 @@ import java.io.RandomAccessFile; public class RandomFileOutputStream extends OutputStream { - RandomAccessFile mFile; - - RandomFileOutputStream(RandomAccessFile file) { - mFile = file; - } - - @Override - public void close() throws IOException { - super.close(); - - mFile.close(); - } + RandomAccessFile mFile; - @Override - public void write(byte[] buffer, int offset, int count) throws IOException { - super.write(buffer, offset, count); - - mFile.write(buffer, offset, count); - } + RandomFileOutputStream(RandomAccessFile file) { + mFile = file; + } - @Override - public void write(byte[] buffer) throws IOException { - super.write(buffer); - - mFile.write(buffer); - } + @Override + public void close() throws IOException { + super.close(); - @Override - public void write(int oneByte) throws IOException { - mFile.write(oneByte); - } - - public void seek(long pos) throws IOException { - mFile.seek(pos); - } + mFile.close(); + } + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + super.write(buffer, offset, count); + + mFile.write(buffer, offset, count); + } + + @Override + public void write(byte[] buffer) throws IOException { + super.write(buffer); + + mFile.write(buffer); + } + + @Override + public void write(int oneByte) throws IOException { + mFile.write(oneByte); + } + + public void seek(long pos) throws IOException { + mFile.seek(pos); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java b/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java index 2adb5f012..3ea26f3cd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -24,19 +24,19 @@ import android.net.Uri; import com.kunzisoft.keepass.database.element.PwDate; public class EmptyUtils { - public static boolean isNullOrEmpty(String str) { - return (str == null) || (str.length() == 0); - } - - public static boolean isNullOrEmpty(byte[] buf) { - return (buf == null) || (buf.length == 0); - } - - public static boolean isNullOrEmpty(PwDate date) { - return (date == null) || date.equals(PwDate.DEFAULT_PWDATE); - } + public static boolean isNullOrEmpty(String str) { + return (str == null) || (str.length() == 0); + } - public static boolean isNullOrEmpty(Uri uri) { - return (uri==null) || (uri.toString().length() == 0); - } + public static boolean isNullOrEmpty(byte[] buf) { + return (buf == null) || (buf.length == 0); + } + + public static boolean isNullOrEmpty(PwDate date) { + return (date == null) || date.equals(PwDate.DEFAULT_PWDATE); + } + + public static boolean isNullOrEmpty(Uri uri) { + return (uri==null) || (uri.toString().length() == 0); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/Interaction.java b/app/src/main/java/com/kunzisoft/keepass/utils/Interaction.java index f498c4e05..2b5a51556 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/Interaction.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/Interaction.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -27,24 +27,24 @@ import android.content.pm.ResolveInfo; import java.util.List; public class Interaction { - /** - * Indicates whether the specified action can be used as an intent. This - * method queries the package manager for installed packages that can - * respond to an intent with the specified action. If no suitable package is - * found, this method returns false. - * - * @param context The application's environment. - * @param action The Intent action to check for availability. - * - * @return True if an Intent with the specified action can be sent and - * responded to, false otherwise. - */ - public static boolean isIntentAvailable(Context context, String action) { - final PackageManager packageManager = context.getPackageManager(); - final Intent intent = new Intent(action); - List list = - packageManager.queryIntentActivities(intent, - PackageManager.MATCH_DEFAULT_ONLY); - return list.size() > 0; - } + /** + * Indicates whether the specified action can be used as an intent. This + * method queries the package manager for installed packages that can + * respond to an intent with the specified action. If no suitable package is + * found, this method returns false. + * + * @param context The application's environment. + * @param action The Intent action to check for availability. + * + * @return True if an Intent with the specified action can be sent and + * responded to, false otherwise. + */ + public static boolean isIntentAvailable(Context context, String action) { + final PackageManager packageManager = context.getPackageManager(); + final Intent intent = new Intent(action); + List list = + packageManager.queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); + return list.size() > 0; + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java index 656e8d166..5f9fa0542 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SprEngineV4.java @@ -1,6 +1,6 @@ /* * Copyright 2019 Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -30,221 +30,221 @@ import java.util.*; import java.util.Map.Entry; public class SprEngineV4 { - private static final int MAX_RECURSION_DEPTH = 12; - private static final String STR_REF_START = "{REF:"; - private static final String STR_REF_END = "}"; + private static final int MAX_RECURSION_DEPTH = 12; + private static final String STR_REF_START = "{REF:"; + private static final String STR_REF_END = "}"; - public class TargetResult { - public PwEntryV4 entry; - public char wanted; + public class TargetResult { + public PwEntryV4 entry; + public char wanted; - public TargetResult(PwEntryV4 entry, char wanted) { - this.entry = entry; - this.wanted = wanted; - } - } + public TargetResult(PwEntryV4 entry, char wanted) { + this.entry = entry; + this.wanted = wanted; + } + } - private class SprContextV4 { + private class SprContextV4 { - public PwDatabaseV4 db; - public PwEntryV4 entry; - public Map refsCache = new HashMap<>(); + public PwDatabaseV4 db; + public PwEntryV4 entry; + public Map refsCache = new HashMap<>(); - SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) { - this.db = db; - this.entry = entry; - } + SprContextV4(PwDatabaseV4 db, PwEntryV4 entry) { + this.db = db; + this.entry = entry; + } - SprContextV4(SprContextV4 source) { - this.db = source.db; - this.entry = source.entry; - this.refsCache = source.refsCache; - } - } + SprContextV4(SprContextV4 source) { + this.db = source.db; + this.entry = source.entry; + this.refsCache = source.refsCache; + } + } - public String compile(String text, PwEntryV4 entry, PwDatabase database) { - SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, entry); + public String compile(String text, PwEntryV4 entry, PwDatabase database) { + SprContextV4 ctx = new SprContextV4((PwDatabaseV4)database, entry); - return compileInternal(text, ctx, 0); - } + return compileInternal(text, ctx, 0); + } - private String compileInternal(String text, SprContextV4 sprContextV4, int recursionLevel) { - if (text == null) { return ""; } - if (sprContextV4 == null) { return ""; } - if (recursionLevel >= MAX_RECURSION_DEPTH) { return ""; } + private String compileInternal(String text, SprContextV4 sprContextV4, int recursionLevel) { + if (text == null) { return ""; } + if (sprContextV4 == null) { return ""; } + if (recursionLevel >= MAX_RECURSION_DEPTH) { return ""; } - return fillRefPlaceholders(text, sprContextV4, recursionLevel); - } + return fillRefPlaceholders(text, sprContextV4, recursionLevel); + } - private String fillRefPlaceholders(String text, SprContextV4 contextV4, int recursionLevel) { + private String fillRefPlaceholders(String text, SprContextV4 contextV4, int recursionLevel) { - if (contextV4.db == null) { return text; } + if (contextV4.db == null) { return text; } - int offset = 0; - for (int i = 0; i < 20; ++i) { - text = fillRefsUsingCache(text, contextV4); + int offset = 0; + for (int i = 0; i < 20; ++i) { + text = fillRefsUsingCache(text, contextV4); - int start = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH); - if (start < 0) { break; } - int end = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH); - if (end <= start) { break; } + int start = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_START, offset, Locale.ENGLISH); + if (start < 0) { break; } + int end = StringUtil.INSTANCE.indexOfIgnoreCase(text, STR_REF_END, start + 1, Locale.ENGLISH); + if (end <= start) { break; } - String fullRef = text.substring(start, end - start + 1); - TargetResult result = findRefTarget(fullRef, contextV4); + String fullRef = text.substring(start, end - start + 1); + TargetResult result = findRefTarget(fullRef, contextV4); - if (result != null) { + if (result != null) { PwEntryV4 found = result.entry; char wanted = result.wanted; if (found != null) { - String data; - switch (wanted) { - case 'T': - data = found.getTitle(); - break; - case 'U': - data = found.getUsername(); - break; - case 'A': - data = found.getUrl(); - break; - case 'P': - data = found.getPassword(); - break; - case 'N': - data = found.getNotes(); - break; - case 'I': - data = found.getNodeId().toString(); - break; - default: - offset = start + 1; - continue; - } + String data; + switch (wanted) { + case 'T': + data = found.getTitle(); + break; + case 'U': + data = found.getUsername(); + break; + case 'A': + data = found.getUrl(); + break; + case 'P': + data = found.getPassword(); + break; + case 'N': + data = found.getNotes(); + break; + case 'I': + data = found.getNodeId().toString(); + break; + default: + offset = start + 1; + continue; + } - SprContextV4 subCtx = new SprContextV4(contextV4); - subCtx.entry = found; + SprContextV4 subCtx = new SprContextV4(contextV4); + subCtx.entry = found; - String innerContent = compileInternal(data, subCtx, recursionLevel + 1); - addRefsToCache(fullRef, innerContent, contextV4); - text = fillRefsUsingCache(text, contextV4); + String innerContent = compileInternal(data, subCtx, recursionLevel + 1); + addRefsToCache(fullRef, innerContent, contextV4); + text = fillRefsUsingCache(text, contextV4); } else { - offset = start + 1; + offset = start + 1; } - } + } - } - - return text; - } - - private TargetResult findRefTarget(String fullRef, SprContextV4 contextV4) { - if (fullRef == null) { return null; } - - fullRef = fullRef.toUpperCase(Locale.ENGLISH); - if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) { - return null; - } - - String ref = fullRef.substring(STR_REF_START.length(), fullRef.length() - STR_REF_START.length() - STR_REF_END.length()); - if (ref.length() <= 4) { return null; } - if (ref.charAt(1) != '@') { return null; } - if (ref.charAt(3) != ':') { return null; } - - char scan = Character.toUpperCase(ref.charAt(2)); - char wanted = Character.toUpperCase(ref.charAt(0)); - - SearchParametersV4 searchParametersV4 = new SearchParametersV4(); - searchParametersV4.setupNone(); - - searchParametersV4.setSearchString(ref.substring(4)); - if (scan == 'T') { searchParametersV4.setSearchInTitles(true); } - else if (scan == 'U') { searchParametersV4.setSearchInUserNames(true); } - else if (scan == 'A') { searchParametersV4.setSearchInUrls(true); } - else if (scan == 'P') { searchParametersV4.setSearchInPasswords(true); } - else if (scan == 'N') { searchParametersV4.setSearchInNotes(true); } - else if (scan == 'I') { searchParametersV4.setSearchInUUIDs(true); } - else if (scan == 'O') { searchParametersV4.setSearchInOther(true); } - else { return null; } - - List list = new ArrayList<>(); - // TODO type parameter - searchEntries(contextV4.db.getRootGroup(), searchParametersV4, list); - - if (list.size() > 0) { - return new TargetResult(list.get(0), wanted); } - return null; - } + return text; + } - private void addRefsToCache(String ref, String value, SprContextV4 ctx) { - if (ref == null) { return; } - if (value == null) { return; } - if (ctx == null) { return; } + private TargetResult findRefTarget(String fullRef, SprContextV4 contextV4) { + if (fullRef == null) { return null; } - if (!ctx.refsCache.containsKey(ref)) { - ctx.refsCache.put(ref, value); - } - } + fullRef = fullRef.toUpperCase(Locale.ENGLISH); + if (!fullRef.startsWith(STR_REF_START) || !fullRef.endsWith(STR_REF_END)) { + return null; + } - private String fillRefsUsingCache(String text, SprContextV4 sprContextV4) { - for (Entry entry : sprContextV4.refsCache.entrySet()) { - text = StringUtil.INSTANCE.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH); - } + String ref = fullRef.substring(STR_REF_START.length(), fullRef.length() - STR_REF_START.length() - STR_REF_END.length()); + if (ref.length() <= 4) { return null; } + if (ref.charAt(1) != '@') { return null; } + if (ref.charAt(3) != ':') { return null; } - return text; - } + char scan = Character.toUpperCase(ref.charAt(2)); + char wanted = Character.toUpperCase(ref.charAt(0)); - private void searchEntries(PwGroupV4 root, SearchParametersV4 searchParametersV4, List listStorage) { - if (searchParametersV4 == null) { return; } - if (listStorage == null) { return; } + SearchParametersV4 searchParametersV4 = new SearchParametersV4(); + searchParametersV4.setupNone(); - List terms = StringUtil.INSTANCE.splitStringTerms(searchParametersV4.getSearchString()); - if (terms.size() <= 1 || searchParametersV4.getRegularExpression()) { - root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, listStorage), null); - return; - } + searchParametersV4.setSearchString(ref.substring(4)); + if (scan == 'T') { searchParametersV4.setSearchInTitles(true); } + else if (scan == 'U') { searchParametersV4.setSearchInUserNames(true); } + else if (scan == 'A') { searchParametersV4.setSearchInUrls(true); } + else if (scan == 'P') { searchParametersV4.setSearchInPasswords(true); } + else if (scan == 'N') { searchParametersV4.setSearchInNotes(true); } + else if (scan == 'I') { searchParametersV4.setSearchInUUIDs(true); } + else if (scan == 'O') { searchParametersV4.setSearchInOther(true); } + else { return null; } - // Search longest term first - Comparator stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length(); - Collections.sort(terms, stringLengthComparator); + List list = new ArrayList<>(); + // TODO type parameter + searchEntries(contextV4.db.getRootGroup(), searchParametersV4, list); - String fullSearch = searchParametersV4.getSearchString(); - List childEntries = root.getChildEntries(); - for (int i = 0; i < terms.size(); i ++) { - List pgNew = new ArrayList<>(); + if (list.size() > 0) { + return new TargetResult(list.get(0), wanted); + } - searchParametersV4.setSearchString(terms.get(i)); + return null; + } - boolean negate = false; - if (searchParametersV4.getSearchString().startsWith("-")) { - searchParametersV4.setSearchString(searchParametersV4.getSearchString().substring(1)); - negate = searchParametersV4.getSearchString().length() > 0; - } + private void addRefsToCache(String ref, String value, SprContextV4 ctx) { + if (ref == null) { return; } + if (value == null) { return; } + if (ctx == null) { return; } - if (!root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, pgNew), null)) { - childEntries = null; - break; - } + if (!ctx.refsCache.containsKey(ref)) { + ctx.refsCache.put(ref, value); + } + } - List complement = new ArrayList<>(); - if (negate) { - for (PwEntryV4 entry: childEntries) { - if (!pgNew.contains(entry)) { - complement.add(entry); - } - } - childEntries = complement; - } - else { - childEntries = pgNew; - } - } + private String fillRefsUsingCache(String text, SprContextV4 sprContextV4) { + for (Entry entry : sprContextV4.refsCache.entrySet()) { + text = StringUtil.INSTANCE.replaceAllIgnoresCase(text, entry.getKey(), entry.getValue(), Locale.ENGLISH); + } - if (childEntries != null) { - listStorage.addAll(childEntries); - } - searchParametersV4.setSearchString(fullSearch); - } + return text; + } + + private void searchEntries(PwGroupV4 root, SearchParametersV4 searchParametersV4, List listStorage) { + if (searchParametersV4 == null) { return; } + if (listStorage == null) { return; } + + List terms = StringUtil.INSTANCE.splitStringTerms(searchParametersV4.getSearchString()); + if (terms.size() <= 1 || searchParametersV4.getRegularExpression()) { + root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, listStorage), null); + return; + } + + // Search longest term first + Comparator stringLengthComparator = (lhs, rhs) -> lhs.length() - rhs.length(); + Collections.sort(terms, stringLengthComparator); + + String fullSearch = searchParametersV4.getSearchString(); + List childEntries = root.getChildEntries(); + for (int i = 0; i < terms.size(); i ++) { + List pgNew = new ArrayList<>(); + + searchParametersV4.setSearchString(terms.get(i)); + + boolean negate = false; + if (searchParametersV4.getSearchString().startsWith("-")) { + searchParametersV4.setSearchString(searchParametersV4.getSearchString().substring(1)); + negate = searchParametersV4.getSearchString().length() > 0; + } + + if (!root.doForEachChild(new EntrySearchHandlerV4(searchParametersV4, pgNew), null)) { + childEntries = null; + break; + } + + List complement = new ArrayList<>(); + if (negate) { + for (PwEntryV4 entry: childEntries) { + if (!pgNew.contains(entry)) { + complement.add(entry); + } + } + childEntries = complement; + } + else { + childEntries = pgNew; + } + } + + if (childEntries != null) { + listStorage.addAll(childEntries); + } + searchParametersV4.setSearchString(fullSearch); + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/Types.java b/app/src/main/java/com/kunzisoft/keepass/utils/Types.java index 18271e031..71eaacf12 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/Types.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/Types.java @@ -52,131 +52,131 @@ import java.util.UUID; /** * Tools for slicing and dicing Java and KeePass data types. - * + * * @author Bill Zwicky */ public class Types { - public static long ULONG_MAX_VALUE = -1; + public static long ULONG_MAX_VALUE = -1; - /** Read an unsigned byte */ - public static int readUByte( byte[] buf, int offset ) { - return ((int)buf[offset] & 0xFF); - } + /** Read an unsigned byte */ + public static int readUByte( byte[] buf, int offset ) { + return ((int)buf[offset] & 0xFF); + } - /** Write an unsigned byte - * - * @param val - * @param buf - * @param offset - */ - public static void writeUByte(int val, byte[] buf, int offset) { - buf[offset] = (byte)(val & 0xFF); - } - - public static byte writeUByte(int val) { - byte[] buf = new byte[1]; - - writeUByte(val, buf, 0); - - return buf[0]; - } + /** Write an unsigned byte + * + * @param val + * @param buf + * @param offset + */ + public static void writeUByte(int val, byte[] buf, int offset) { + buf[offset] = (byte)(val & 0xFF); + } - /** - * Return len of null-terminated string (i.e. distance to null) - * within a byte buffer. - * - * @param buf - * @param offset - * @return - */ - public static int strlen( byte[] buf, int offset ) { - int len = 0; - while( buf[offset + len] != 0 ) - len++; - return len; - } + public static byte writeUByte(int val) { + byte[] buf = new byte[1]; + + writeUByte(val, buf, 0); + + return buf[0]; + } + + /** + * Return len of null-terminated string (i.e. distance to null) + * within a byte buffer. + * + * @param buf + * @param offset + * @return + */ + public static int strlen( byte[] buf, int offset ) { + int len = 0; + while( buf[offset + len] != 0 ) + len++; + return len; + } - /** - * Copy a sequence of bytes into a new array. - * - * @param b - source array - * @param offset - first byte - * @param len - number of bytes - * @return new byte[len] - */ - public static byte[] extract( byte[] b, int offset, int len ) { - byte[] b2 = new byte[len]; - System.arraycopy( b, offset, b2, 0, len ); - return b2; - } - - - private static final byte[] CRLFbuf = { 0x0D, 0x0A }; - private static final String CRLF = new String(CRLFbuf); - private static final String SEP = System.getProperty("line.separator"); - private static final boolean REPLACE = ! SEP.equals(CRLF); - - public static String readCString(byte[] buf, int offset) throws UnsupportedEncodingException { - String jstring = new String(buf, offset, strlen(buf, offset), "UTF-8"); - - if ( REPLACE ) { - jstring = jstring.replace(CRLF, SEP); - } - - return jstring; - } + /** + * Copy a sequence of bytes into a new array. + * + * @param b - source array + * @param offset - first byte + * @param len - number of bytes + * @return new byte[len] + */ + public static byte[] extract( byte[] b, int offset, int len ) { + byte[] b2 = new byte[len]; + System.arraycopy( b, offset, b2, 0, len ); + return b2; + } - public static int writeCString(String str, OutputStream os) throws IOException { - if ( str == null ) { - // Write out a null character - os.write(LEDataOutputStream.writeIntBuf(1)); - os.write(0x00); - return 0; - } - - if ( REPLACE ) { - str = str.replace(SEP, CRLF); - } - - byte[] initial = str.getBytes("UTF-8"); - - int length = initial.length+1; - os.write(LEDataOutputStream.writeIntBuf(length)); - os.write(initial); - os.write(0x00); - - return length; - } - - public static UUID bytestoUUID(byte[] buf) { - return bytestoUUID(buf, 0); - } - - public static UUID bytestoUUID(byte[] buf, int offset) { - long lsb = 0; - for (int i = 15; i >= 8; i--) { - lsb = (lsb << 8) | (buf[i + offset] & 0xff); - } - - long msb = 0; - for (int i = 7; i >= 0; i--) { - msb = (msb << 8) | (buf[i + offset] & 0xff); - } - return new UUID(msb, lsb); + private static final byte[] CRLFbuf = { 0x0D, 0x0A }; + private static final String CRLF = new String(CRLFbuf); + private static final String SEP = System.getProperty("line.separator"); + private static final boolean REPLACE = ! SEP.equals(CRLF); + + public static String readCString(byte[] buf, int offset) throws UnsupportedEncodingException { + String jstring = new String(buf, offset, strlen(buf, offset), "UTF-8"); + + if ( REPLACE ) { + jstring = jstring.replace(CRLF, SEP); + } + + return jstring; + } + + public static int writeCString(String str, OutputStream os) throws IOException { + if ( str == null ) { + // Write out a null character + os.write(LEDataOutputStream.writeIntBuf(1)); + os.write(0x00); + return 0; + } + + if ( REPLACE ) { + str = str.replace(SEP, CRLF); + } + + byte[] initial = str.getBytes("UTF-8"); + + int length = initial.length+1; + os.write(LEDataOutputStream.writeIntBuf(length)); + os.write(initial); + os.write(0x00); + + return length; + } + + public static UUID bytestoUUID(byte[] buf) { + return bytestoUUID(buf, 0); + } + + public static UUID bytestoUUID(byte[] buf, int offset) { + long lsb = 0; + for (int i = 15; i >= 8; i--) { + lsb = (lsb << 8) | (buf[i + offset] & 0xff); + } + + long msb = 0; + for (int i = 7; i >= 0; i--) { + msb = (msb << 8) | (buf[i + offset] & 0xff); + } + + return new UUID(msb, lsb); + + } + + public static byte[] UUIDtoBytes(UUID uuid) { + byte[] buf = new byte[16]; + + LEDataOutputStream.writeLong(uuid.getMostSignificantBits(), buf, 0); + LEDataOutputStream.writeLong(uuid.getLeastSignificantBits(), buf, 8); + + return buf; + } - } - - public static byte[] UUIDtoBytes(UUID uuid) { - byte[] buf = new byte[16]; - - LEDataOutputStream.writeLong(uuid.getMostSignificantBits(), buf, 0); - LEDataOutputStream.writeLong(uuid.getLeastSignificantBits(), buf, 8); - - return buf; - } - } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/Util.java b/app/src/main/java/com/kunzisoft/keepass/utils/Util.java index ae8c899f0..bffdec0c3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/Util.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/Util.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -34,18 +34,18 @@ import com.kunzisoft.keepass.R; public class Util { - public static void gotoUrl(Context context, String url) throws ActivityNotFoundException { - if ( url != null && url.length() > 0 ) { - Uri uri = Uri.parse(url); - context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); - } - } - - public static void gotoUrl(Context context, int resId) throws ActivityNotFoundException { - gotoUrl(context, context.getString(resId)); - } + public static void gotoUrl(Context context, String url) throws ActivityNotFoundException { + if ( url != null && url.length() > 0 ) { + Uri uri = Uri.parse(url); + context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + } + } - /** + public static void gotoUrl(Context context, int resId) throws ActivityNotFoundException { + gotoUrl(context, context.getString(resId)); + } + + /** * Replace font by monospace, must be called after seText() */ public static void applyFontVisibilityTo(final Context context, final TextView textView) { @@ -56,28 +56,28 @@ public class Util { /** * Replace font by monospace, must be called after seText() */ - public static void applyFontVisibilityTo(final Context context, final EditText editText) { + public static void applyFontVisibilityTo(final Context context, final EditText editText) { applyFontVisibilityTo(context, (TextView) editText); - } + } - public static float getListTextDefaultSize(Context context) { - return Float.parseFloat(context.getString(R.string.list_size_default)); - } + public static float getListTextDefaultSize(Context context) { + return Float.parseFloat(context.getString(R.string.list_size_default)); + } public static void lockScreenOrientation(Activity activity) { - if (activity != null) { - int currentOrientation = activity.getResources().getConfiguration().orientation; - if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) { + if (activity != null) { + int currentOrientation = activity.getResources().getConfiguration().orientation; + if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } else { + } else { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); - } - } - } + } + } + } public static void unlockScreenOrientation(Activity activity) { if (activity != null) { activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } - } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UuidUtil.java b/app/src/main/java/com/kunzisoft/keepass/utils/UuidUtil.java index 0caa8b448..b61875e1a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/UuidUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UuidUtil.java @@ -1,6 +1,6 @@ /* * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * + * * This file is part of KeePass DX. * * KeePass DX is free software: you can redistribute it and/or modify @@ -22,44 +22,44 @@ package com.kunzisoft.keepass.utils; import java.util.UUID; public class UuidUtil { - public static String toHexString(UUID uuid) { - if (uuid == null) { return null; } - - byte[] buf = Types.UUIDtoBytes(uuid); - if (buf == null) { return null; } - - int len = buf.length; - if (len == 0) { return ""; } - - StringBuilder sb = new StringBuilder(); - - short bt; - char high, low; - for (int i = 0; i < len; i++) { - bt = (short)(buf[i] & 0xFF); - high = (char)(bt >>> 4); - - - low = (char)(bt & 0x0F); - - char h,l; - h = byteToChar(high); - l = byteToChar(low); + public static String toHexString(UUID uuid) { + if (uuid == null) { return null; } - sb.append(byteToChar(high)); - sb.append(byteToChar(low)); - } - - return sb.toString(); - } - - // Use short to represent unsigned byte - private static char byteToChar(char bt) { + byte[] buf = Types.UUIDtoBytes(uuid); + if (buf == null) { return null; } + + int len = buf.length; + if (len == 0) { return ""; } + + StringBuilder sb = new StringBuilder(); + + short bt; + char high, low; + for (int i = 0; i < len; i++) { + bt = (short)(buf[i] & 0xFF); + high = (char)(bt >>> 4); + + + low = (char)(bt & 0x0F); + + char h,l; + h = byteToChar(high); + l = byteToChar(low); + + sb.append(byteToChar(high)); + sb.append(byteToChar(low)); + } + + return sb.toString(); + } + + // Use short to represent unsigned byte + private static char byteToChar(char bt) { if (bt >= 10) { return (char)('A' + bt - 10); } else { return (char)('0' + bt); } - } + } } From cdf8baeb108f8b432a0bef419b9361156a38a09a Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 4 Jul 2019 15:25:47 +0200 Subject: [PATCH 129/289] Kotlinized PwDate --- .../tests/{PwDateTest.java => PwDateTest.kt} | 23 +- .../kunzisoft/keepass/tests/TypesTest.java | 4 +- .../keepass/activities/EntryActivity.java | 2 +- .../java/com/kunzisoft/keepass/app/App.java | 10 - .../keepass/database/SortNodeEnum.kt | 18 +- .../keepass/database/element/PwDate.java | 266 ---------------- .../keepass/database/element/PwDate.kt | 284 ++++++++++++++++++ .../keepass/database/element/PwEntryV4.kt | 2 +- .../keepass/database/element/PwNode.kt | 3 +- .../database/file/save/PwEntryOutputV3.kt | 8 +- .../database/file/save/PwGroupOutputV3.kt | 8 +- .../fileselect/FileSelectActivity.java | 2 +- .../keepass/password/PasswordActivity.java | 6 +- .../utils/{EmptyUtils.java => EmptyUtils.kt} | 24 +- .../com/kunzisoft/keepass/utils/UriUtil.java | 19 +- 15 files changed, 342 insertions(+), 337 deletions(-) rename app/src/androidTest/java/com/kunzisoft/keepass/tests/{PwDateTest.java => PwDateTest.kt} (59%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.kt rename app/src/main/java/com/kunzisoft/keepass/utils/{EmptyUtils.java => EmptyUtils.kt} (57%) diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.kt similarity index 59% rename from app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java rename to app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.kt index 492c4e5f2..05a2993c7 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwDateTest.kt @@ -17,22 +17,21 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.tests; +package com.kunzisoft.keepass.tests -import junit.framework.TestCase; +import junit.framework.TestCase -import com.kunzisoft.keepass.database.element.PwDate; +import com.kunzisoft.keepass.database.element.PwDate +import org.junit.Assert -public class PwDateTest extends TestCase { - public void testDate() { - PwDate jDate = new PwDate(System.currentTimeMillis()); +class PwDateTest : TestCase() { - PwDate intermediate = new PwDate(jDate); - - PwDate cDate = new PwDate(intermediate.getCDate(), 0); - - assertTrue("jDate and intermediate not equal", jDate.equals(intermediate)); - assertTrue("jDate and cDate not equal", cDate.equals(jDate)); + fun testDate() { + val jDate = PwDate(System.currentTimeMillis()) + val intermediate = PwDate(jDate) + val cDate = PwDate(intermediate.byteArrayDate!!, 0) + Assert.assertTrue("jDate and intermediate not equal", jDate == intermediate) + Assert.assertTrue("jDate and cDate not equal", cDate == jDate) } } diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java index aef346519..66f792b8b 100644 --- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java +++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java @@ -170,9 +170,9 @@ public class TypesTest extends TestCase { Calendar expected = Calendar.getInstance(); expected.set(2008, 1, 2, 3, 4, 5); - byte[] buf = PwDate.writeTime(expected.getTime(), cal); + byte[] buf = PwDate.Companion.writeTime(expected.getTime(), cal); Calendar actual = Calendar.getInstance(); - actual.setTime(PwDate.readTime(buf, 0, cal)); + actual.setTime(PwDate.Companion.readTime(buf, 0, cal)); assertEquals("Year mismatch: ", 2008, actual.get(Calendar.YEAR)); assertEquals("Month mismatch: ", 1, actual.get(Calendar.MONTH)); diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 4ee2698da..6a5c6d020 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -452,7 +452,7 @@ public class EntryActivity extends LockingHideActivity { gotoUrl.setVisible(false); } else { String url = mEntry.getUrl(); - if (EmptyUtils.isNullOrEmpty(url)) { + if (EmptyUtils.INSTANCE.isNullOrEmpty(url)) { // disable button if url is not available gotoUrl.setVisible(false); } diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.java b/app/src/main/java/com/kunzisoft/keepass/app/App.java index 2d888d74d..93a4aba37 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.java +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.java @@ -26,11 +26,8 @@ import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.fileselect.RecentFileHistory; import com.kunzisoft.keepass.stylish.Stylish; -import java.util.Calendar; - public class App extends MultiDexApplication { private static Database db = null; - private static Calendar calendar = null; private static RecentFileHistory fileHistory = null; public static Database getDB() { @@ -48,13 +45,6 @@ public class App extends MultiDexApplication { db = d; } - public static Calendar getCalendar() { - if ( calendar == null ) { - calendar = Calendar.getInstance(); - } - return calendar; - } - @Override public void onCreate() { super.onCreate(); 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 5afd0ea7e..22a6b2415 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/SortNodeEnum.kt @@ -111,7 +111,7 @@ enum class SortNodeEnum { object1, object2, object1.creationTime.date - .compareTo(object2.creationTime.date)) + ?.compareTo(object2.creationTime.date) ?: 0) } } @@ -128,7 +128,7 @@ enum class SortNodeEnum { object1, object2, object1.lastModificationTime.date - .compareTo(object2.lastModificationTime.date)) + ?.compareTo(object2.lastModificationTime.date) ?: 0) } } @@ -145,7 +145,7 @@ enum class SortNodeEnum { object1, object2, object1.lastAccessTime.date - .compareTo(object2.lastAccessTime.date)) + ?.compareTo(object2.lastAccessTime.date) ?: 0) } } @@ -186,7 +186,7 @@ enum class SortNodeEnum { return 0 val groupCreationComp = object1.creationTime.date - .compareTo(object2.creationTime.date) + ?.compareTo(object2.creationTime.date) ?: 0 // If same creation, can be different return if (groupCreationComp == 0) { object1.hashCode() - object2.hashCode() @@ -205,7 +205,7 @@ enum class SortNodeEnum { return 0 val groupLastModificationComp = object1.lastModificationTime.date - .compareTo(object2.lastModificationTime.date) + ?.compareTo(object2.lastModificationTime.date) ?: 0 // If same creation, can be different return if (groupLastModificationComp == 0) { object1.hashCode() - object2.hashCode() @@ -224,7 +224,7 @@ enum class SortNodeEnum { return 0 val groupLastAccessComp = object1.lastAccessTime.date - .compareTo(object2.lastAccessTime.date) + ?.compareTo(object2.lastAccessTime.date) ?: 0 // If same creation, can be different return if (groupLastAccessComp == 0) { object1.hashCode() - object2.hashCode() @@ -261,7 +261,7 @@ enum class SortNodeEnum { return 0 val entryCreationComp = object1.creationTime.date - .compareTo(object2.creationTime.date) + ?.compareTo(object2.creationTime.date) ?: 0 // If same creation, can be different return if (entryCreationComp == 0) { object1.hashCode() - object2.hashCode() @@ -280,7 +280,7 @@ enum class SortNodeEnum { return 0 val entryLastModificationComp = object1.lastModificationTime.date - .compareTo(object2.lastModificationTime.date) + ?.compareTo(object2.lastModificationTime.date) ?: 0 // If same creation, can be different return if (entryLastModificationComp == 0) { object1.hashCode() - object2.hashCode() @@ -299,7 +299,7 @@ enum class SortNodeEnum { return 0 val entryLastAccessComp = object1.lastAccessTime.date - .compareTo(object2.lastAccessTime.date) + ?.compareTo(object2.lastAccessTime.date) ?: 0 // If same creation, can be different return if (entryLastAccessComp == 0) { object1.hashCode() - object2.hashCode() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java deleted file mode 100644 index 8e535faad..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.database.element; - -import android.os.Parcel; -import android.os.Parcelable; - -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.utils.Types; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; - -/** Converting from the C Date format to the Java data format is - * expensive when done for every record at once. I use this class to - * allow lazy conversions between the formats. - * @author bpellin - * - */ -public class PwDate implements Parcelable { - - private static final int DATE_SIZE = 5; - - private Date jDate = null; - private boolean jDateBuilt = false; - transient private byte[] cDate = null; - transient private boolean cDateBuilt = false; - - public static final Date NEVER_EXPIRE = getNeverExpire(); - public static final Date DEFAULT_DATE = getDefaultDate(); - - public static final PwDate PW_NEVER_EXPIRE = new PwDate(NEVER_EXPIRE); - public static final PwDate DEFAULT_PWDATE = new PwDate(DEFAULT_DATE); - - private static Date getDefaultDate() { - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.YEAR, 2004); - cal.set(Calendar.MONTH, Calendar.JANUARY); - cal.set(Calendar.DAY_OF_MONTH, 1); - cal.set(Calendar.HOUR, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - - return cal.getTime(); - } - - private static Date getNeverExpire() { - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.YEAR, 2999); - cal.set(Calendar.MONTH, 11); - cal.set(Calendar.DAY_OF_MONTH, 28); - cal.set(Calendar.HOUR, 23); - cal.set(Calendar.MINUTE, 59); - cal.set(Calendar.SECOND, 59); - - return cal.getTime(); - } - - public PwDate(byte[] buf, int offset) { - cDate = new byte[DATE_SIZE]; - System.arraycopy(buf, offset, cDate, 0, DATE_SIZE); - cDateBuilt = true; - } - - public PwDate(PwDate source) { - if (source.jDate != null) { - this.jDate = new Date(source.jDate.getTime()); - } - this.jDateBuilt = source.jDateBuilt; - - if (source.cDate != null) { - int dateLength = source.cDate.length; - this.cDate = new byte[dateLength]; - System.arraycopy(source.cDate, 0, this.cDate, 0, dateLength); - } - this.cDateBuilt = source.cDateBuilt; - } - - public PwDate(Date date) { - jDate = new Date(date.getTime()); - jDateBuilt = true; - } - - public PwDate(long millis) { - jDate = new Date(millis); - jDateBuilt = true; - } - - public PwDate() { - jDate = new Date(); - jDateBuilt = true; - } - - protected PwDate(Parcel in) { - jDate = (Date) in.readSerializable(); - jDateBuilt = in.readByte() != 0; - cDateBuilt = false; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeSerializable(getDate()); - dest.writeByte((byte) (jDateBuilt ? 1 : 0)); - } - - public static final Creator CREATOR = new Creator() { - @Override - public PwDate createFromParcel(Parcel in) { - return new PwDate(in); - } - - @Override - public PwDate[] newArray(int size) { - return new PwDate[size]; - } - }; - - public Date getDate() { - if ( ! jDateBuilt ) { - jDate = readTime(cDate, 0, App.getCalendar()); - jDateBuilt = true; - } - - return jDate; - } - - public byte[] getCDate() { - if ( ! cDateBuilt ) { - cDate = writeTime(jDate, App.getCalendar()); - cDateBuilt = true; - } - - return cDate; - } - - /** - * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked - * to a java.util.Date instance. - */ - public static Date readTime(byte[] buf, int offset, Calendar time) { - int dw1 = Types.readUByte(buf, offset); - int dw2 = Types.readUByte(buf, offset + 1); - int dw3 = Types.readUByte(buf, offset + 2); - int dw4 = Types.readUByte(buf, offset + 3); - int dw5 = Types.readUByte(buf, offset + 4); - - // Unpack 5 byte structure to date and time - int year = (dw1 << 6) | (dw2 >> 2); - int month = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); - - int day = (dw3 >> 1) & 0x0000001F; - int hour = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); - int minute = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); - int second = dw5 & 0x0000003F; - - if (time == null) { - time = Calendar.getInstance(); - } - // File format is a 1 based month, java Calendar uses a zero based month - // File format is a 1 based day, java Calendar uses a 1 based day - time.set(year, month - 1, day, hour, minute, second); - - return time.getTime(); - - } - - public static byte[] writeTime(Date date) { - return writeTime(date, null); - } - - public static byte[] writeTime(Date date, Calendar cal) { - if (date == null) { - return null; - } - - byte[] buf = new byte[5]; - if (cal == null) { - cal = Calendar.getInstance(); - } - cal.setTime(date); - - int year = cal.get(Calendar.YEAR); - // File format is a 1 based month, java Calendar uses a zero based month - int month = cal.get(Calendar.MONTH) + 1; - // File format is a 0 based day, java Calendar uses a 1 based day - int day = cal.get(Calendar.DAY_OF_MONTH) - 1; - int hour = cal.get(Calendar.HOUR_OF_DAY); - int minute = cal.get(Calendar.MINUTE); - int second = cal.get(Calendar.SECOND); - - buf[0] = Types.writeUByte(((year >> 6) & 0x0000003F)); - buf[1] = Types.writeUByte(((year & 0x0000003F) << 2) - | ((month >> 2) & 0x00000003)); - buf[2] = (byte) (((month & 0x00000003) << 6) - | ((day & 0x0000001F) << 1) | ((hour >> 4) & 0x00000001)); - buf[3] = (byte) (((hour & 0x0000000F) << 4) | ((minute >> 2) & 0x0000000F)); - buf[4] = (byte) (((minute & 0x00000003) << 6) | (second & 0x0000003F)); - - return buf; - } - - @Override - public boolean equals(Object o) { - if ( this == o ) { - return true; - } - if ( o == null ) { - return false; - } - if ( getClass() != o.getClass() ) { - return false; - } - - PwDate date = (PwDate) o; - if ( cDateBuilt && date.cDateBuilt ) { - return Arrays.equals(cDate, date.cDate); - } else if ( jDateBuilt && date.jDateBuilt ) { - return IsSameDate(jDate, date.jDate); - } else if ( cDateBuilt && date.jDateBuilt ) { - return Arrays.equals(date.getCDate(), cDate); - } else { - return IsSameDate(date.getDate(), jDate); - } - } - - private static boolean IsSameDate(Date d1, Date d2) { - Calendar cal1 = Calendar.getInstance(); - cal1.setTime(d1); - cal1.set(Calendar.MILLISECOND, 0); - - Calendar cal2 = Calendar.getInstance(); - cal2.setTime(d2); - cal2.set(Calendar.MILLISECOND, 0); - - return (cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)) && - (cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH)) && - (cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH)) && - (cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR)) && - (cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE)) && - (cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)); - - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.kt new file mode 100644 index 000000000..1c2ec3c47 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDate.kt @@ -0,0 +1,284 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.database.element + +import android.os.Parcel +import android.os.Parcelable + +import com.kunzisoft.keepass.utils.Types + +import java.util.Arrays +import java.util.Calendar +import java.util.Date + +/** + * Converting from the C Date format to the Java data format is + * expensive when done for every record at once. + */ +class PwDate : Parcelable { + + private var jDate: Date? = null + private var jDateBuilt = false + @Transient + private var cDate: ByteArray? = null + @Transient + private var cDateBuilt = false + + val date: Date? + get() { + if (!jDateBuilt) { + jDate = readTime(cDate, 0, calendar) + jDateBuilt = true + } + + return jDate + } + + val byteArrayDate: ByteArray? + get() { + if (!cDateBuilt) { + cDate = writeTime(jDate, calendar) + cDateBuilt = true + } + + return cDate + } + + constructor(buf: ByteArray, offset: Int) { + cDate = ByteArray(DATE_SIZE) + System.arraycopy(buf, offset, cDate!!, 0, DATE_SIZE) + cDateBuilt = true + } + + constructor(source: PwDate) { + if (source.jDate != null) { + this.jDate = Date(source.jDate!!.time) + } + this.jDateBuilt = source.jDateBuilt + + if (source.cDate != null) { + val dateLength = source.cDate!!.size + this.cDate = ByteArray(dateLength) + System.arraycopy(source.cDate!!, 0, this.cDate!!, 0, dateLength) + } + this.cDateBuilt = source.cDateBuilt + } + + constructor(date: Date) { + jDate = Date(date.time) + jDateBuilt = true + } + + constructor(millis: Long) { + jDate = Date(millis) + jDateBuilt = true + } + + constructor() { + jDate = Date() + jDateBuilt = true + } + + protected constructor(parcel: Parcel) { + jDate = parcel.readSerializable() as Date + jDateBuilt = parcel.readByte().toInt() != 0 + cDateBuilt = false + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeSerializable(date) + dest.writeByte((if (jDateBuilt) 1 else 0).toByte()) + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null) { + return false + } + if (javaClass != other.javaClass) { + return false + } + + val date = other as PwDate? + return if (cDateBuilt && date!!.cDateBuilt) { + Arrays.equals(cDate, date.cDate) + } else if (jDateBuilt && date!!.jDateBuilt) { + isSameDate(jDate, date.jDate) + } else if (cDateBuilt && date!!.jDateBuilt) { + Arrays.equals(date.byteArrayDate, cDate) + } else { + isSameDate(date!!.date, jDate) + } + } + + override fun hashCode(): Int { + var result = jDate?.hashCode() ?: 0 + result = 31 * result + jDateBuilt.hashCode() + result = 31 * result + (cDate?.contentHashCode() ?: 0) + result = 31 * result + cDateBuilt.hashCode() + return result + } + + companion object { + + private const val DATE_SIZE = 5 + + private var mCalendar: Calendar? = null + + val NEVER_EXPIRE = neverExpire + val DEFAULT_DATE = defaultDate + + val PW_NEVER_EXPIRE = PwDate(NEVER_EXPIRE) + val DEFAULT_PWDATE = PwDate(DEFAULT_DATE) + + private val calendar: Calendar? + get() { + if (mCalendar == null) { + mCalendar = Calendar.getInstance() + } + return mCalendar + } + + private val defaultDate: Date + get() { + val cal = Calendar.getInstance() + cal.set(Calendar.YEAR, 2004) + cal.set(Calendar.MONTH, Calendar.JANUARY) + cal.set(Calendar.DAY_OF_MONTH, 1) + cal.set(Calendar.HOUR, 0) + cal.set(Calendar.MINUTE, 0) + cal.set(Calendar.SECOND, 0) + + return cal.time + } + + private val neverExpire: Date + get() { + val cal = Calendar.getInstance() + cal.set(Calendar.YEAR, 2999) + cal.set(Calendar.MONTH, 11) + cal.set(Calendar.DAY_OF_MONTH, 28) + cal.set(Calendar.HOUR, 23) + cal.set(Calendar.MINUTE, 59) + cal.set(Calendar.SECOND, 59) + + return cal.time + } + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): PwDate { + return PwDate(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + /** + * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked + * to a java.util.Date instance. + */ + fun readTime(buf: ByteArray?, offset: Int, calendar: Calendar?): Date { + var time = calendar + val dw1 = Types.readUByte(buf!!, offset) + val dw2 = Types.readUByte(buf, offset + 1) + val dw3 = Types.readUByte(buf, offset + 2) + val dw4 = Types.readUByte(buf, offset + 3) + val dw5 = Types.readUByte(buf, offset + 4) + + // Unpack 5 byte structure to date and time + val year = dw1 shl 6 or (dw2 shr 2) + val month = dw2 and 0x00000003 shl 2 or (dw3 shr 6) + + val day = dw3 shr 1 and 0x0000001F + val hour = dw3 and 0x00000001 shl 4 or (dw4 shr 4) + val minute = dw4 and 0x0000000F shl 2 or (dw5 shr 6) + val second = dw5 and 0x0000003F + + if (time == null) { + time = Calendar.getInstance() + } + // File format is a 1 based month, java Calendar uses a zero based month + // File format is a 1 based day, java Calendar uses a 1 based day + time!!.set(year, month - 1, day, hour, minute, second) + + return time.time + + } + + @JvmOverloads + fun writeTime(date: Date?, calendar: Calendar? = null): ByteArray? { + var cal = calendar + if (date == null) { + return null + } + + val buf = ByteArray(5) + if (cal == null) { + cal = Calendar.getInstance() + } + cal!!.time = date + + val year = cal.get(Calendar.YEAR) + // File format is a 1 based month, java Calendar uses a zero based month + val month = cal.get(Calendar.MONTH) + 1 + // File format is a 0 based day, java Calendar uses a 1 based day + val day = cal.get(Calendar.DAY_OF_MONTH) - 1 + val hour = cal.get(Calendar.HOUR_OF_DAY) + val minute = cal.get(Calendar.MINUTE) + val second = cal.get(Calendar.SECOND) + + buf[0] = Types.writeUByte(year shr 6 and 0x0000003F) + buf[1] = Types.writeUByte(year and 0x0000003F shl 2 or (month shr 2 and 0x00000003)) + buf[2] = (month and 0x00000003 shl 6 + or (day and 0x0000001F shl 1) or (hour shr 4 and 0x00000001)).toByte() + buf[3] = (hour and 0x0000000F shl 4 or (minute shr 2 and 0x0000000F)).toByte() + buf[4] = (minute and 0x00000003 shl 6 or (second and 0x0000003F)).toByte() + + return buf + } + + private fun isSameDate(d1: Date?, d2: Date?): Boolean { + val cal1 = Calendar.getInstance() + cal1.time = d1 + cal1.set(Calendar.MILLISECOND, 0) + + val cal2 = Calendar.getInstance() + cal2.time = d2 + cal2.set(Calendar.MILLISECOND, 0) + + return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH) && + cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH) && + cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR) && + cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && + cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) + + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt index 663cff5b8..7348b00a9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwEntryV4.kt @@ -283,7 +283,7 @@ class PwEntryV4 : PwEntry, NodeV4Interface { for (i in history.indices) { val entry = history[i] val lastMod = entry.lastModificationTime.date - if (min == null || lastMod.before(min)) { + if (min == null || lastMod == null || lastMod.before(min)) { index = i min = lastMod } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt index 9a019bab2..aa0b11a42 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwNode.kt @@ -88,7 +88,8 @@ abstract class PwNode, Entry : final override var isExpires: Boolean // If expireDate is before NEVER_EXPIRE date less 1 month (to be sure) - get() = expiryTime.date.before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()) + get() = expiryTime.date + ?.before(LocalDate.fromDateFields(PwDate.NEVER_EXPIRE).minusMonths(1).toDate()) ?: true set(value) { if (!value) { expiryTime = PwDate.PW_NEVER_EXPIRE diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt index a24c7c1e6..578048ac8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwEntryOutputV3.kt @@ -89,16 +89,16 @@ class PwEntryOutputV3 length += addlLen.toLong() // Create date - writeDate(CREATE_FIELD_TYPE, mPE.creationTime.cDate) + writeDate(CREATE_FIELD_TYPE, mPE.creationTime.byteArrayDate) // Modification date - writeDate(MOD_FIELD_TYPE, mPE.lastModificationTime.cDate) + writeDate(MOD_FIELD_TYPE, mPE.lastModificationTime.byteArrayDate) // Access date - writeDate(ACCESS_FIELD_TYPE, mPE.lastAccessTime.cDate) + writeDate(ACCESS_FIELD_TYPE, mPE.lastAccessTime.byteArrayDate) // Expiration date - writeDate(EXPIRE_FIELD_TYPE, mPE.expiryTime.cDate) + writeDate(EXPIRE_FIELD_TYPE, mPE.expiryTime.byteArrayDate) // Binary desc mOS.write(BINARY_DESC_FIELD_TYPE) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt index 247b9c5ae..bbd936539 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwGroupOutputV3.kt @@ -49,22 +49,22 @@ class PwGroupOutputV3 // Create date mOS.write(CREATE_FIELD_TYPE) mOS.write(DATE_FIELD_SIZE) - mOS.write(mPG.creationTime.cDate) + mOS.write(mPG.creationTime.byteArrayDate) // Modification date mOS.write(MOD_FIELD_TYPE) mOS.write(DATE_FIELD_SIZE) - mOS.write(mPG.lastModificationTime.cDate) + mOS.write(mPG.lastModificationTime.byteArrayDate) // Access date mOS.write(ACCESS_FIELD_TYPE) mOS.write(DATE_FIELD_SIZE) - mOS.write(mPG.lastAccessTime.cDate) + mOS.write(mPG.lastAccessTime.byteArrayDate) // Expiration date mOS.write(EXPIRE_FIELD_TYPE) mOS.write(DATE_FIELD_SIZE) - mOS.write(mPG.expiryTime.cDate) + mOS.write(mPG.expiryTime.byteArrayDate) // Image ID mOS.write(IMAGEID_FIELD_TYPE) diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java index 1c7d4c2ea..84e2a9c44 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java @@ -218,7 +218,7 @@ public class FileSelectActivity extends StylishActivity implements if (dbUri != null) scheme = dbUri.getScheme(); - if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { + if (!EmptyUtils.INSTANCE.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { String path = dbUri.getPath(); File db = new File(path); diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index dd66947db..54e222f87 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -131,7 +131,7 @@ public class PasswordActivity extends StylishActivity } private static void verifyFileNameUriFromLaunch(String fileName) throws FileNotFoundException { - if (EmptyUtils.isNullOrEmpty(fileName)) { + if (EmptyUtils.INSTANCE.isNullOrEmpty(fileName)) { throw new FileNotFoundException(); } @@ -139,7 +139,7 @@ public class PasswordActivity extends StylishActivity assert uri != null; String scheme = uri.getScheme(); - if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { + if (!EmptyUtils.INSTANCE.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { File dbFile = new File(uri.getPath()); if (!dbFile.exists()) { throw new FileNotFoundException(); @@ -471,7 +471,7 @@ public class PasswordActivity extends StylishActivity // Retrieve settings for default database String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, ""); if (mDbUri!=null - && !EmptyUtils.isNullOrEmpty(mDbUri.getPath()) + && !EmptyUtils.INSTANCE.isNullOrEmpty(mDbUri.getPath()) && UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) { checkboxDefaultDatabaseView.setChecked(true); } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java b/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.kt similarity index 57% rename from app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java rename to app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.kt index 3ea26f3cd..e946af741 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/EmptyUtils.kt @@ -17,26 +17,26 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.utils; +package com.kunzisoft.keepass.utils -import android.net.Uri; +import android.net.Uri -import com.kunzisoft.keepass.database.element.PwDate; +import com.kunzisoft.keepass.database.element.PwDate -public class EmptyUtils { - public static boolean isNullOrEmpty(String str) { - return (str == null) || (str.length() == 0); +object EmptyUtils { + fun isNullOrEmpty(str: String?): Boolean { + return str == null || str.isEmpty() } - public static boolean isNullOrEmpty(byte[] buf) { - return (buf == null) || (buf.length == 0); + fun isNullOrEmpty(buf: ByteArray?): Boolean { + return buf == null || buf.isEmpty() } - public static boolean isNullOrEmpty(PwDate date) { - return (date == null) || date.equals(PwDate.DEFAULT_PWDATE); + fun isNullOrEmpty(date: PwDate?): Boolean { + return date == null || date == PwDate.DEFAULT_PWDATE } - public static boolean isNullOrEmpty(Uri uri) { - return (uri==null) || (uri.toString().length() == 0); + fun isNullOrEmpty(uri: Uri?): Boolean { + return uri == null || uri.toString().isEmpty() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java index 91b4838a8..f59857b20 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.java @@ -26,28 +26,25 @@ import android.net.Uri; import com.kunzisoft.keepass.compat.StorageAF; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; /** * Created by bpellin on 3/5/16. */ public class UriUtil { public static Uri parseDefaultFile(String text) { - if (EmptyUtils.isNullOrEmpty(text)) { + if (EmptyUtils.INSTANCE.isNullOrEmpty(text)) { return null; } Uri uri = Uri.parse(text); - if (EmptyUtils.isNullOrEmpty(uri.getScheme())) { + if (EmptyUtils.INSTANCE.isNullOrEmpty(uri.getScheme())) { uri = uri.buildUpon().scheme("file").authority("").build(); } return uri; } public static Uri parseDefaultFile(Uri uri) { - if (EmptyUtils.isNullOrEmpty(uri.getScheme())) { + if (EmptyUtils.INSTANCE.isNullOrEmpty(uri.getScheme())) { uri = uri.buildUpon().scheme("file").authority("").build(); } @@ -72,7 +69,7 @@ public class UriUtil { if (StorageAF.useStorageFramework(ctx) || hasWritableContentUri(uri)) { return uri; } String scheme = uri.getScheme(); - if (EmptyUtils.isNullOrEmpty(scheme)) { return uri; } + if (EmptyUtils.INSTANCE.isNullOrEmpty(scheme)) { return uri; } String filepath = null; @@ -94,7 +91,7 @@ public class UriUtil { } // Try using the URI path as a straight file - if (EmptyUtils.isNullOrEmpty(filepath)) { + if (EmptyUtils.INSTANCE.isNullOrEmpty(filepath)) { filepath = uri.getEncodedPath(); if (!isValidFilePath(filepath)) { filepath = null; @@ -107,7 +104,7 @@ public class UriUtil { } // Update the file to a file URI - if (!EmptyUtils.isNullOrEmpty(filepath)) { + if (!EmptyUtils.INSTANCE.isNullOrEmpty(filepath)) { Uri.Builder b = new Uri.Builder(); uri = b.scheme("file").authority("").path(filepath).build(); } @@ -116,7 +113,7 @@ public class UriUtil { } private static boolean isValidFilePath(String filepath) { - if (EmptyUtils.isNullOrEmpty(filepath)) { return false; } + if (EmptyUtils.INSTANCE.isNullOrEmpty(filepath)) { return false; } File file = new File(filepath); return file.exists() && file.canRead(); @@ -130,7 +127,7 @@ public class UriUtil { private static boolean hasWritableContentUri(Uri uri) { String scheme = uri.getScheme(); - if (EmptyUtils.isNullOrEmpty(scheme)) { return false; } + if (EmptyUtils.INSTANCE.isNullOrEmpty(scheme)) { return false; } if (!scheme.equalsIgnoreCase("content")) { return false; } From 15794f09c38b14abcd49fa550e95e509915a6630 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 4 Jul 2019 19:04:42 +0200 Subject: [PATCH 130/289] Move database history in singleton --- app/src/main/AndroidManifest.xml | 2 +- .../keepass/activities/EntryActivity.java | 6 +- .../keepass/activities/EntryEditActivity.java | 4 +- .../keepass/activities/GroupActivity.java | 24 +- .../activities/lock/LockingActivity.kt | 6 +- .../keepass/adapters/NodeAdapter.java | 4 +- .../java/com/kunzisoft/keepass/app/App.java | 64 ---- .../FileSelectViewHolder.java => app/App.kt} | 35 ++- .../autofill/AutoFillLauncherActivity.kt | 6 +- .../database/action/CreateDatabaseRunnable.kt | 7 +- .../database/action/LoadDatabaseRunnable.kt | 27 +- .../dialogs/GroupEditDialogFragment.java | 2 +- .../DeleteFileHistoryAsyncTask.java | 14 +- ...r.java => FileDatabaseHistoryAdapter.java} | 44 +-- .../FileDatabaseHistoryViewHolder.kt | 34 +++ ...SelectBean.java => FileDatabaseModel.java} | 4 +- ...y.java => FileDatabaseSelectActivity.java} | 91 +++--- .../FileInformationDialogFragment.java | 18 +- .../fileselect/OpenFileHistoryAsyncTask.java | 8 +- .../keepass/fileselect/RecentFileHistory.java | 273 ------------------ .../FileDatabaseHelper.java} | 81 +----- .../database/FileDatabaseHistory.kt | 259 +++++++++++++++++ .../database/FileDatabaseHistoryHelper.java | 58 ++++ .../keepass/icons/IconPackChooser.java | 2 +- .../magikeyboard/KeyboardLauncherActivity.kt | 6 +- .../keepass/password/PasswordActivity.java | 89 +----- .../keepass/password/UriIntentInitTask.kt | 89 ++++++ .../settings/MainPreferenceFragment.java | 2 +- .../settings/NestedSettingsFragment.java | 9 +- ...abaseSavePreferenceDialogFragmentCompat.kt | 2 +- .../keepass/timeout/TimeoutHelper.kt | 4 +- .../keepass/utils/SingletonHolder.kt | 17 ++ gradle.properties | 2 +- 33 files changed, 647 insertions(+), 646 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/app/App.java rename app/src/main/java/com/kunzisoft/keepass/{fileselect/FileSelectViewHolder.java => app/App.kt} (50%) rename app/src/main/java/com/kunzisoft/keepass/fileselect/{FileSelectAdapter.java => FileDatabaseHistoryAdapter.java} (74%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryViewHolder.kt rename app/src/main/java/com/kunzisoft/keepass/fileselect/{FileSelectBean.java => FileDatabaseModel.java} (95%) rename app/src/main/java/com/kunzisoft/keepass/fileselect/{FileSelectActivity.java => FileDatabaseSelectActivity.java} (89%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java rename app/src/main/java/com/kunzisoft/keepass/fileselect/{FileDbHelper.java => database/FileDatabaseHelper.java} (72%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistoryHelper.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTask.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/SingletonHolder.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 69bdaf58e..399f808f7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,7 +27,7 @@ android:value="" /> { }); // Database - this.database = App.getDB(); + this.database = App.Companion.getCurrentDatabase(); // Retrieve the color to tint the icon int[] attrTextColorPrimary = {android.R.attr.textColorPrimary}; @@ -340,7 +340,7 @@ public class NodeAdapter extends RecyclerView.Adapter { MenuItem menuItem = contextMenu.findItem(R.id.menu_open); menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - Database database = App.getDB(); + Database database = App.Companion.getCurrentDatabase(); // Edition if (readOnly || node.equals(database.getRecycleBin())) { diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.java b/app/src/main/java/com/kunzisoft/keepass/app/App.java deleted file mode 100644 index 93a4aba37..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.app; - -import android.support.multidex.MultiDexApplication; - -import com.kunzisoft.keepass.compat.PRNGFixes; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.fileselect.RecentFileHistory; -import com.kunzisoft.keepass.stylish.Stylish; - -public class App extends MultiDexApplication { - private static Database db = null; - private static RecentFileHistory fileHistory = null; - - public static Database getDB() { - if ( db == null ) { - db = new Database(); - } - return db; - } - - public static RecentFileHistory getFileHistory() { - return fileHistory; - } - - public static void setDB(Database d) { - db = d; - } - - @Override - public void onCreate() { - super.onCreate(); - - Stylish.init(this); - fileHistory = new RecentFileHistory(this); - PRNGFixes.apply(); - } - - @Override - public void onTerminate() { - if ( db != null ) { - db.closeAndClear(getApplicationContext()); - } - super.onTerminate(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectViewHolder.java b/app/src/main/java/com/kunzisoft/keepass/app/App.kt similarity index 50% rename from app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectViewHolder.java rename to app/src/main/java/com/kunzisoft/keepass/app/App.kt index efe29e1b0..0709203a7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectViewHolder.java +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,25 +17,28 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.fileselect; +package com.kunzisoft.keepass.app -import android.support.v7.widget.RecyclerView; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; +import android.support.multidex.MultiDexApplication +import com.kunzisoft.keepass.compat.PRNGFixes +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.stylish.Stylish -import com.kunzisoft.keepass.R; +class App : MultiDexApplication() { -class FileSelectViewHolder extends RecyclerView.ViewHolder { + companion object { + var currentDatabase: Database = Database() + } - View fileContainer; - TextView fileName; - ImageView fileInformation; + override fun onCreate() { + super.onCreate() - FileSelectViewHolder(View itemView) { - super(itemView); - fileContainer = itemView.findViewById(R.id.file_container); - fileName = (TextView) itemView.findViewById(R.id.file_filename); - fileInformation = (ImageView) itemView.findViewById(R.id.file_information); + Stylish.init(this) + PRNGFixes.apply() + } + + override fun onTerminate() { + currentDatabase.closeAndClear(applicationContext) + super.onTerminate() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt index 22919c23e..b107d413c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt @@ -30,7 +30,7 @@ import android.support.annotation.RequiresApi import android.support.v7.app.AppCompatActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.fileselect.FileSelectActivity +import com.kunzisoft.keepass.fileselect.FileDatabaseSelectActivity import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper @@ -41,10 +41,10 @@ class AutoFillLauncherActivity : AppCompatActivity() { // Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE) val assistStructure = AutofillHelper.retrieveAssistStructure(intent) if (assistStructure != null) { - if (App.getDB().loaded && TimeoutHelper.checkTime(this)) + if (App.currentDatabase.loaded && TimeoutHelper.checkTime(this)) GroupActivity.launchForAutofillResult(this, assistStructure, PreferencesUtil.enableReadOnlyDatabase(this)) else { - FileSelectActivity.launchForAutofillResult(this, assistStructure) + FileDatabaseSelectActivity.launchForAutofillResult(this, assistStructure) } } else { setResult(Activity.RESULT_CANCELED) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt index 3ab478d89..6f06eb0c5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt @@ -27,14 +27,11 @@ class CreateDatabaseRunnable(private val mFilename: String, val onDatabaseCreate: (database: Database) -> ActionRunnable) : ActionRunnable() { - var database: Database? = null - override fun run() { try { // Create new database record - database = Database(mFilename) - App.setDB(database) - database?.apply { + Database(mFilename).apply { + App.currentDatabase = this // Set Database state loaded = true // Commit changes diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt index 71c068923..eee176f18 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt @@ -25,15 +25,16 @@ import android.preference.PreferenceManager import android.support.annotation.StringRes import android.util.Log import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.* +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import java.io.FileNotFoundException import java.io.IOException +import java.lang.ref.WeakReference -class LoadDatabaseRunnable(private val mContext: Context, +class LoadDatabaseRunnable(private val mWeakContext: WeakReference, private val mDatabase: Database, private val mUri: Uri, private val mPass: String?, @@ -42,14 +43,22 @@ class LoadDatabaseRunnable(private val mContext: Context, nestedAction: ActionRunnable) : ActionRunnable(nestedAction, executeNestedActionIfResultFalse = true) { - private val mRememberKeyFile: Boolean = PreferenceManager.getDefaultSharedPreferences(mContext) - .getBoolean(mContext.getString(R.string.keyfile_key), mContext.resources.getBoolean(R.bool.keyfile_default)) + private val mRememberKeyFile: Boolean + get() { + return mWeakContext.get()?.let { + PreferenceManager.getDefaultSharedPreferences(it) + .getBoolean(it.getString(R.string.keyfile_key), + it.resources.getBoolean(R.bool.keyfile_default)) + } ?: true + } override fun run() { try { - mDatabase.loadData(mContext, mUri, mPass, mKey, progressTaskUpdater) - saveFileData(mUri, mKey) - finishRun(true) + mWeakContext.get()?.let { + mDatabase.loadData(it, mUri, mPass, mKey, progressTaskUpdater) + saveFileData(mUri, mKey) + finishRun(true) + } ?: finishRun(false, "Context null") } catch (e: ArcFourException) { catchError(e, R.string.error_arc4) return @@ -98,7 +107,7 @@ class LoadDatabaseRunnable(private val mContext: Context, } private fun catchError(e: Throwable, @StringRes messageId: Int, addThrowableMessage: Boolean = false) { - var errorMessage = mContext.getString(messageId) + var errorMessage = mWeakContext.get()?.getString(messageId) Log.e(TAG, errorMessage, e) if (addThrowableMessage) errorMessage = errorMessage + " " + e.localizedMessage @@ -110,7 +119,7 @@ class LoadDatabaseRunnable(private val mContext: Context, if (!mRememberKeyFile) { keyFileUri = null } - App.getFileHistory().createFile(uri, keyFileUri) + FileDatabaseHistory.getInstance(mWeakContext).addDatabaseUri(uri, keyFileUri) } override fun onFinishRun(isSuccess: Boolean, message: String?) {} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index 354e36f56..fa576a400 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -118,7 +118,7 @@ public class GroupEditDialogFragment extends DialogFragment iconColor = ta.getColor(0, Color.WHITE); // Init elements - database = App.getDB(); + database = App.Companion.getCurrentDatabase(); editGroupDialogAction = EditGroupDialogAction.NONE; nameGroup = ""; iconGroup = database.getIconFactory().getFolderIcon(); diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java index f8ceed45a..3d4ffad56 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java @@ -21,20 +21,22 @@ package com.kunzisoft.keepass.fileselect; import android.os.AsyncTask; -class DeleteFileHistoryAsyncTask extends AsyncTask { +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; + +class DeleteFileHistoryAsyncTask extends AsyncTask { private AfterDeleteFileHistoryListener afterDeleteFileHistoryListener; - private RecentFileHistory fileHistory; - private FileSelectAdapter adapter; + private FileDatabaseHistory fileHistory; + private FileDatabaseHistoryAdapter adapter; - DeleteFileHistoryAsyncTask(AfterDeleteFileHistoryListener afterDeleteFileHistoryListener, RecentFileHistory fileHistory, FileSelectAdapter adapter) { + DeleteFileHistoryAsyncTask(AfterDeleteFileHistoryListener afterDeleteFileHistoryListener, FileDatabaseHistory fileHistory, FileDatabaseHistoryAdapter adapter) { this.afterDeleteFileHistoryListener = afterDeleteFileHistoryListener; this.fileHistory = fileHistory; this.adapter = adapter; } - protected Void doInBackground(FileSelectBean... args) { - fileHistory.deleteFile(args[0].getFileUri()); + protected Void doInBackground(FileDatabaseModel... args) { + fileHistory.deleteDatabaseUri(args[0].getFileUri()); return null; } diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectAdapter.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.java similarity index 74% rename from app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectAdapter.java rename to app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.java index 468586d48..4c69b2cbc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectAdapter.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.java @@ -38,7 +38,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil; import java.util.List; -public class FileSelectAdapter extends RecyclerView.Adapter { +public class FileDatabaseHistoryAdapter extends RecyclerView.Adapter { private static final int MENU_CLEAR = 1; @@ -54,7 +54,7 @@ public class FileSelectAdapter extends RecyclerView.Adapter listFiles) { + FileDatabaseHistoryAdapter(Context context, List listFiles) { this.inflater = LayoutInflater.from(context); this.context = context; this.listFiles = listFiles; @@ -69,28 +69,28 @@ public class FileSelectAdapter extends RecyclerView.Adapter. + * + */ +package com.kunzisoft.keepass.fileselect + +import android.support.v7.widget.RecyclerView +import android.view.View +import android.widget.ImageView +import android.widget.TextView + +import com.kunzisoft.keepass.R + +internal class FileDatabaseHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var fileContainer: View = itemView.findViewById(R.id.file_container) + var fileName: TextView = itemView.findViewById(R.id.file_filename) + var fileInformation: ImageView = itemView.findViewById(R.id.file_information) +} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectBean.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseModel.java similarity index 95% rename from app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectBean.java rename to app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseModel.java index c62d9c978..07de0acd8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectBean.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseModel.java @@ -27,7 +27,7 @@ import java.io.File; import java.io.Serializable; import java.util.Date; -public class FileSelectBean implements Serializable { +public class FileDatabaseModel implements Serializable { private static final String EXTERNAL_STORAGE_AUTHORITY = "com.android.externalstorage.documents"; @@ -36,7 +36,7 @@ public class FileSelectBean implements Serializable { private Date lastModification; private long size; - public FileSelectBean(Context context, String pathFile) { + public FileDatabaseModel(Context context, String pathFile) { fileName = ""; lastModification = new Date(); size = 0; diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java similarity index 89% rename from app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java rename to app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java index 84e2a9c44..80f0b323a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java @@ -50,10 +50,10 @@ import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.activities.GroupActivity; -import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable; +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; @@ -73,6 +73,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.ref.WeakReference; import java.net.URLDecoder; import permissions.dispatcher.NeedsPermission; @@ -83,24 +84,24 @@ import permissions.dispatcher.PermissionRequest; import permissions.dispatcher.RuntimePermissions; @RuntimePermissions -public class FileSelectActivity extends StylishActivity implements +public class FileDatabaseSelectActivity extends StylishActivity implements CreateFileDialogFragment.DefinePathDialogListener, AssignMasterKeyDialogFragment.AssignPasswordDialogListener, - FileSelectAdapter.FileItemOpenListener, - FileSelectAdapter.FileSelectClearListener, - FileSelectAdapter.FileInformationShowListener { + FileDatabaseHistoryAdapter.FileItemOpenListener, + FileDatabaseHistoryAdapter.FileSelectClearListener, + FileDatabaseHistoryAdapter.FileInformationShowListener { - private static final String TAG = "FileSelectActivity"; + private static final String TAG = "FileDatabaseSelectActivity"; private static final String EXTRA_STAY = "EXTRA_STAY"; - private FileSelectAdapter mAdapter; + private FileDatabaseHistoryAdapter mAdapter; private View fileListContainer; private View createButtonView; private View browseButtonView; private View openButtonView; - private RecentFileHistory fileHistory; + private FileDatabaseHistory fileDatabaseHistory; private View fileSelectExpandableButton; private ExpandableLayout fileSelectExpandable; @@ -125,7 +126,7 @@ public class FileSelectActivity extends StylishActivity implements */ public static void launchForKeyboardSelection(Activity activity) { - KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, new Intent(activity, FileSelectActivity.class)); + KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, new Intent(activity, FileDatabaseSelectActivity.class)); } /* @@ -137,7 +138,7 @@ public class FileSelectActivity extends StylishActivity implements @RequiresApi(api = Build.VERSION_CODES.O) public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure) { AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, - new Intent(activity, FileSelectActivity.class), + new Intent(activity, FileDatabaseSelectActivity.class), assistStructure); } @@ -145,7 +146,7 @@ public class FileSelectActivity extends StylishActivity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - fileHistory = App.getFileHistory(); + fileDatabaseHistory = FileDatabaseHistory.Companion.getInstance(new WeakReference<>(getApplicationContext())); setContentView(R.layout.file_selection); fileListContainer = findViewById(R.id.container_file_list); @@ -189,8 +190,8 @@ public class FileSelectActivity extends StylishActivity implements // Create button createButtonView = findViewById(R.id.create_database); createButtonView .setOnClickListener(v -> - FileSelectActivityPermissionsDispatcher - .openCreateFileDialogFragmentWithPermissionCheck(FileSelectActivity.this) + FileDatabaseSelectActivityPermissionsDispatcher + .openCreateFileDialogFragmentWithPermissionCheck(FileDatabaseSelectActivity.this) ); keyFileHelper = new KeyFileHelper(this); @@ -199,7 +200,7 @@ public class FileSelectActivity extends StylishActivity implements () -> Uri.parse("file://" + openFileNameView.getText().toString()))); // Construct adapter with listeners - mAdapter = new FileSelectAdapter(FileSelectActivity.this, fileHistory.getDbList()); + mAdapter = new FileDatabaseHistoryAdapter(FileDatabaseSelectActivity.this, fileDatabaseHistory.getDatabaseUriList()); mAdapter.setOnItemClickListener(this); mAdapter.setFileSelectClearListener(this); mAdapter.setFileInformationShowListener(this); @@ -238,7 +239,7 @@ public class FileSelectActivity extends StylishActivity implements private void fileNoFoundAction(FileNotFoundException e) { String error = getString(R.string.file_not_found_content); - Toast.makeText(FileSelectActivity.this, + Toast.makeText(FileDatabaseSelectActivity.this, error, Toast.LENGTH_LONG).show(); Log.e(TAG, error, e); } @@ -247,7 +248,7 @@ public class FileSelectActivity extends StylishActivity implements EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), () -> { try { - PasswordActivity.launch(FileSelectActivity.this, + PasswordActivity.launch(FileDatabaseSelectActivity.this, fileName, keyFile); } catch (FileNotFoundException e) { fileNoFoundAction(e); @@ -256,7 +257,7 @@ public class FileSelectActivity extends StylishActivity implements }, () -> { try { - PasswordActivity.launchForKeyboardResult(FileSelectActivity.this, + PasswordActivity.launchForKeyboardResult(FileDatabaseSelectActivity.this, fileName, keyFile); finish(); } catch (FileNotFoundException e) { @@ -267,7 +268,7 @@ public class FileSelectActivity extends StylishActivity implements assistStructure -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { - PasswordActivity.launchForAutofillResult(FileSelectActivity.this, + PasswordActivity.launchForAutofillResult(FileDatabaseSelectActivity.this, fileName, keyFile, assistStructure); } catch (FileNotFoundException e) { @@ -320,7 +321,7 @@ public class FileSelectActivity extends StylishActivity implements private void checkAndPerformedEducation() { // If no recent files - if ( !fileHistory.hasRecentFiles() ) { + if ( !fileDatabaseHistory.hasRecentFiles() ) { // Try to open the creation base education if (!PreferencesUtil.isEducationCreateDatabasePerformed(this) ) { @@ -336,8 +337,8 @@ public class FileSelectActivity extends StylishActivity implements @Override public void onTargetClick(TapTargetView view) { super.onTargetClick(view); - FileSelectActivityPermissionsDispatcher - .openCreateFileDialogFragmentWithPermissionCheck(FileSelectActivity.this); + FileDatabaseSelectActivityPermissionsDispatcher + .openCreateFileDialogFragmentWithPermissionCheck(FileDatabaseSelectActivity.this); } @Override @@ -348,7 +349,7 @@ public class FileSelectActivity extends StylishActivity implements checkAndPerformedEducationForSelection(); } }); - PreferencesUtil.saveEducationPreference(FileSelectActivity.this, + PreferencesUtil.saveEducationPreference(FileDatabaseSelectActivity.this, R.string.education_create_db_key); } } @@ -366,7 +367,7 @@ public class FileSelectActivity extends StylishActivity implements if (!PreferencesUtil.isEducationSelectDatabasePerformed(this) && browseButtonView != null) { - TapTargetView.showFor(FileSelectActivity.this, + TapTargetView.showFor(FileDatabaseSelectActivity.this, TapTarget.forView(browseButtonView, getString(R.string.education_select_database_title), getString(R.string.education_select_database_summary)) @@ -386,13 +387,13 @@ public class FileSelectActivity extends StylishActivity implements super.onOuterCircleClick(view); view.dismiss(false); - if (!PreferencesUtil.isEducationOpenLinkDatabasePerformed(FileSelectActivity.this)) { + if (!PreferencesUtil.isEducationOpenLinkDatabasePerformed(FileDatabaseSelectActivity.this)) { - TapTargetView.showFor(FileSelectActivity.this, + TapTargetView.showFor(FileDatabaseSelectActivity.this, TapTarget.forView(fileSelectExpandableButton, getString(R.string.education_open_link_database_title), getString(R.string.education_open_link_database_summary)) - .icon(ContextCompat.getDrawable(FileSelectActivity.this, R.drawable.ic_link_white_24dp)) + .icon(ContextCompat.getDrawable(FileDatabaseSelectActivity.this, R.drawable.ic_link_white_24dp)) .textColorInt(Color.WHITE) .tintTarget(true) .cancelable(true), @@ -409,12 +410,12 @@ public class FileSelectActivity extends StylishActivity implements view.dismiss(false); } }); - PreferencesUtil.saveEducationPreference(FileSelectActivity.this, + PreferencesUtil.saveEducationPreference(FileDatabaseSelectActivity.this, R.string.education_open_link_db_key); } } }); - PreferencesUtil.saveEducationPreference(FileSelectActivity.this, + PreferencesUtil.saveEducationPreference(FileDatabaseSelectActivity.this, R.string.education_select_db_key); } } @@ -431,7 +432,7 @@ public class FileSelectActivity extends StylishActivity implements public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); // NOTE: delegate the permission handling to generated method - FileSelectActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); + FileDatabaseSelectActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); } @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -457,7 +458,7 @@ public class FileSelectActivity extends StylishActivity implements // Make sure file name exists if (pathString.length() == 0) { Log.e(TAG, getString(R.string.error_filename_required)); - Toast.makeText(FileSelectActivity.this, + Toast.makeText(FileDatabaseSelectActivity.this, R.string.error_filename_required, Toast.LENGTH_LONG).show(); return false; @@ -468,7 +469,7 @@ public class FileSelectActivity extends StylishActivity implements try { if (file.exists()) { Log.e(TAG, getString(R.string.error_database_exists) + " " + file); - Toast.makeText(FileSelectActivity.this, + Toast.makeText(FileDatabaseSelectActivity.this, R.string.error_database_exists, Toast.LENGTH_LONG).show(); return false; @@ -477,7 +478,7 @@ public class FileSelectActivity extends StylishActivity implements if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) { Log.e(TAG, getString(R.string.error_invalid_path) + " " + file); - Toast.makeText(FileSelectActivity.this, + Toast.makeText(FileDatabaseSelectActivity.this, R.string.error_invalid_path, Toast.LENGTH_LONG).show(); return false; @@ -487,7 +488,7 @@ public class FileSelectActivity extends StylishActivity implements // Create parent directory if ( ! parent.mkdirs() ) { Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent); - Toast.makeText(FileSelectActivity.this, + Toast.makeText(FileDatabaseSelectActivity.this, R.string.error_could_not_create_parent, Toast.LENGTH_LONG).show(); return false; @@ -499,7 +500,7 @@ public class FileSelectActivity extends StylishActivity implements Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.getLocalizedMessage()); e.printStackTrace(); Toast.makeText( - FileSelectActivity.this, + FileDatabaseSelectActivity.this, getText(R.string.error_file_not_create) + " " + e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); @@ -538,7 +539,7 @@ public class FileSelectActivity extends StylishActivity implements progressTaskUpdater -> new CreateDatabaseRunnable(databaseFilename, database -> { // TODO store database created - return new AssignPasswordInDatabaseRunnable(FileSelectActivity.this, + return new AssignPasswordInDatabaseRunnable(FileDatabaseSelectActivity.this, database, masterPasswordChecked, masterPassword, @@ -578,10 +579,10 @@ public class FileSelectActivity extends StylishActivity implements runOnUiThread(() -> { if (isSuccess) { // Add database to recent files - fileHistory.createFile(fileURI); + fileDatabaseHistory.addDatabaseUri(fileURI); mAdapter.notifyDataSetChanged(); updateFileListVisibility(); - GroupActivity.launch(FileSelectActivity.this); + GroupActivity.launch(FileDatabaseSelectActivity.this); } else { Log.e(TAG, "Unable to open the database"); } @@ -601,25 +602,25 @@ public class FileSelectActivity extends StylishActivity implements new OpenFileHistoryAsyncTask((fileName, keyFile) -> { launchPasswordActivity(fileName, keyFile); updateFileListVisibility(); - }, fileHistory).execute(itemPosition); + }, fileDatabaseHistory).execute(itemPosition); } @Override - public void onClickFileInformation(FileSelectBean fileSelectBean) { - if (fileSelectBean != null) { + public void onClickFileInformation(FileDatabaseModel fileDatabaseModel) { + if (fileDatabaseModel != null) { FileInformationDialogFragment fileInformationDialogFragment = - FileInformationDialogFragment.newInstance(fileSelectBean); + FileInformationDialogFragment.newInstance(fileDatabaseModel); fileInformationDialogFragment.show(getSupportFragmentManager(), "fileInformation"); } } @Override - public boolean onFileSelectClearListener(final FileSelectBean fileSelectBean) { + public boolean onFileSelectClearListener(final FileDatabaseModel fileDatabaseModel) { new DeleteFileHistoryAsyncTask(() -> { - fileHistory.deleteFile(fileSelectBean.getFileUri()); + fileDatabaseHistory.deleteDatabaseUri(fileDatabaseModel.getFileUri()); mAdapter.notifyDataSetChanged(); updateFileListVisibility(); - }, fileHistory, mAdapter).execute(fileSelectBean); + }, fileDatabaseHistory, mAdapter).execute(fileDatabaseModel); return true; } @@ -634,7 +635,7 @@ public class FileSelectActivity extends StylishActivity implements keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, uri -> { if (uri != null) { - if (PreferencesUtil.autoOpenSelectedFile(FileSelectActivity.this)) { + if (PreferencesUtil.autoOpenSelectedFile(FileDatabaseSelectActivity.this)) { launchPasswordActivityWithPath(uri.toString()); } else { fileSelectExpandable.expand(false); diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileInformationDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileInformationDialogFragment.java index 726fa1b79..0e793117d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileInformationDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileInformationDialogFragment.java @@ -40,11 +40,11 @@ public class FileInformationDialogFragment extends DialogFragment { private View fileSizeContainerView; private View fileModificationContainerView; - public static FileInformationDialogFragment newInstance(FileSelectBean fileSelectBean) { + public static FileInformationDialogFragment newInstance(FileDatabaseModel fileDatabaseModel) { FileInformationDialogFragment fileInformationDialogFragment = new FileInformationDialogFragment(); Bundle args = new Bundle(); - args.putSerializable(FILE_SELECT_BEEN_ARG, fileSelectBean); + args.putSerializable(FILE_SELECT_BEEN_ARG, fileDatabaseModel); fileInformationDialogFragment.setArguments(args); return fileInformationDialogFragment; } @@ -63,19 +63,19 @@ public class FileInformationDialogFragment extends DialogFragment { TextView fileModificationView = root.findViewById(R.id.file_modification); if (getArguments() != null && getArguments().containsKey(FILE_SELECT_BEEN_ARG)) { - FileSelectBean fileSelectBean = (FileSelectBean) getArguments().getSerializable(FILE_SELECT_BEEN_ARG); - if(fileSelectBean != null) { + FileDatabaseModel fileDatabaseModel = (FileDatabaseModel) getArguments().getSerializable(FILE_SELECT_BEEN_ARG); + if(fileDatabaseModel != null) { - filePathView.setText(Uri.decode(fileSelectBean.getFileUri().toString())); - fileNameView.setText(fileSelectBean.getFileName()); + filePathView.setText(Uri.decode(fileDatabaseModel.getFileUri().toString())); + fileNameView.setText(fileDatabaseModel.getFileName()); - if(fileSelectBean.notFound()) { + if(fileDatabaseModel.notFound()) { hideFileInfo(); } else { showFileInfo(); - fileSizeView.setText(String.valueOf(fileSelectBean.getSize())); + fileSizeView.setText(String.valueOf(fileDatabaseModel.getSize())); fileModificationView.setText(DateFormat.getDateTimeInstance() - .format(fileSelectBean.getLastModification())); + .format(fileDatabaseModel.getLastModification())); } } else hideFileInfo(); diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java index 872ef8179..39169eed7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java @@ -21,14 +21,16 @@ package com.kunzisoft.keepass.fileselect; import android.os.AsyncTask; +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; + class OpenFileHistoryAsyncTask extends AsyncTask { private AfterOpenFileHistoryListener afterOpenFileHistoryListener; - private RecentFileHistory fileHistory; + private FileDatabaseHistory fileHistory; private String fileName; private String keyFile; - OpenFileHistoryAsyncTask(AfterOpenFileHistoryListener afterOpenFileHistoryListener, RecentFileHistory fileHistory) { + OpenFileHistoryAsyncTask(AfterOpenFileHistoryListener afterOpenFileHistoryListener, FileDatabaseHistory fileHistory) { this.afterOpenFileHistoryListener = afterOpenFileHistoryListener; this.fileHistory = fileHistory; } @@ -36,7 +38,7 @@ class OpenFileHistoryAsyncTask extends AsyncTask { protected Void doInBackground(Integer... args) { int position = args[0]; fileName = fileHistory.getDatabaseAt(position); - keyFile = fileHistory.getKeyfileAt(position); + keyFile = fileHistory.getKeyFileAt(position); return null; } diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java deleted file mode 100644 index 381107d80..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/RecentFileHistory.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.fileselect; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.database.Cursor; -import android.net.Uri; -import android.preference.PreferenceManager; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.utils.UriUtil; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class RecentFileHistory { - - private static String DB_KEY = "recent_databases"; - private static String KEYFILE_KEY = "recent_keyfiles"; - - private List databases = new ArrayList(); - private List keyfiles = new ArrayList(); - private Context ctx; - private SharedPreferences prefs; - private OnSharedPreferenceChangeListener listner; - private boolean enabled; - private boolean init = false; - - public RecentFileHistory(Context c) { - ctx = c.getApplicationContext(); - - prefs = PreferenceManager.getDefaultSharedPreferences(c); - enabled = prefs.getBoolean(ctx.getString(R.string.recentfile_key), ctx.getResources().getBoolean(R.bool.recentfile_default)); - listner = new OnSharedPreferenceChangeListener() { - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, - String key) { - if (key.equals(ctx.getString(R.string.recentfile_key))) { - enabled = sharedPreferences.getBoolean(ctx.getString(R.string.recentfile_key), ctx.getResources().getBoolean(R.bool.recentfile_default)); - } - } - }; - prefs.registerOnSharedPreferenceChangeListener(listner); - } - - private synchronized void init() { - if (!init) { - if (!upgradeFromSQL()) { - loadPrefs(); - } - - init = true; - } - } - - private boolean upgradeFromSQL() { - - try { - // Check for a database to upgrade from - if (!sqlDatabaseExists()) { - return false; - } - - databases.clear(); - keyfiles.clear(); - - FileDbHelper helper = new FileDbHelper(ctx); - helper.open(); - Cursor cursor = helper.fetchAllFiles(); - - int dbIndex = cursor.getColumnIndex(FileDbHelper.KEY_FILE_FILENAME); - int keyIndex = cursor.getColumnIndex(FileDbHelper.KEY_FILE_KEYFILE); - - if(cursor.moveToFirst()) { - while (cursor.moveToNext()) { - String filename = cursor.getString(dbIndex); - String keyfile = cursor.getString(keyIndex); - - databases.add(filename); - keyfiles.add(keyfile); - } - } - - savePrefs(); - - cursor.close(); - helper.close(); - - } catch (Exception e) { - // If upgrading fails, we'll just give up on it. - } - - try { - FileDbHelper.deleteDatabase(ctx); - } catch (Exception e) { - // If we fail to delete it, just move on - } - - return true; - } - - private boolean sqlDatabaseExists() { - File db = ctx.getDatabasePath(FileDbHelper.DATABASE_NAME); - return db.exists(); - } - - public void createFile(Uri databaseUri) { - createFile(databaseUri, null); - } - - public void createFile(Uri databaseUri, Uri keyFileUri) { - if (!enabled || databaseUri == null) return; - - init(); - - // Remove any existing instance of the same filename - deleteFile(databaseUri, false); - - databases.add(0, databaseUri.toString()); - - String key = (keyFileUri == null) ? "" : keyFileUri.toString(); - keyfiles.add(0, key); - - trimLists(); - savePrefs(); - } - - public boolean hasRecentFiles() { - if (!enabled) return false; - - init(); - - return databases.size() > 0; - } - - public String getDatabaseAt(int i) { - init(); - return databases.get(i); - } - - public String getKeyfileAt(int i) { - init(); - return keyfiles.get(i); - } - - private void loadPrefs() { - loadList(databases, DB_KEY); - loadList(keyfiles, KEYFILE_KEY); - } - - private void savePrefs() { - saveList(DB_KEY, databases); - saveList(KEYFILE_KEY, keyfiles); - } - - private void loadList(List list, String keyprefix) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - int size = prefs.getInt(keyprefix, 0); - - list.clear(); - for (int i = 0; i < size; i++) { - list.add(prefs.getString(keyprefix + "_" + i, "")); - } - } - - private void saveList(String keyprefix, List list) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); - SharedPreferences.Editor edit = prefs.edit(); - int size = list.size(); - edit.putInt(keyprefix, size); - - for (int i = 0; i < size; i++) { - edit.putString(keyprefix + "_" + i, list.get(i)); - } - edit.apply(); - } - - public void deleteFile(Uri uri) { - deleteFile(uri, true); - } - - public void deleteFile(Uri uri, boolean save) { - init(); - - String uriName = uri.toString(); - String fileName = uri.getPath(); - - for (int i = 0; i < databases.size(); i++) { - String entry = databases.get(i); - if (uriName.equals(entry) || fileName.equals(entry)) { - databases.remove(i); - keyfiles.remove(i); - break; - } - } - - if (save) { - savePrefs(); - } - } - - public List getDbList() { - init(); - - return databases; - } - - public Uri getFileByName(Uri database) { - if (!enabled) return null; - - init(); - - int size = databases.size(); - for (int i = 0; i < size; i++) { - if (UriUtil.equalsDefaultfile(database,databases.get(i))) { - return UriUtil.parseDefaultFile(keyfiles.get(i)); - } - } - - return null; - } - - public void deleteAll() { - init(); - - databases.clear(); - keyfiles.clear(); - - savePrefs(); - } - - public void deleteAllKeys() { - init(); - - keyfiles.clear(); - - int size = databases.size(); - for (int i = 0; i < size; i++) { - keyfiles.add(""); - } - - savePrefs(); - } - - private void trimLists() { - int size = databases.size(); - for (int i = FileDbHelper.MAX_FILES; i < size; i++) { - databases.remove(i); - keyfiles.remove(i); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDbHelper.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHelper.java similarity index 72% rename from app/src/main/java/com/kunzisoft/keepass/fileselect/FileDbHelper.java rename to app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHelper.java index 2738173b6..cad5ae967 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDbHelper.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHelper.java @@ -17,30 +17,27 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.fileselect; +package com.kunzisoft.keepass.fileselect.database; import android.content.ContentValues; import android.content.Context; -import android.content.SharedPreferences; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; import java.io.File; import java.io.FileFilter; -public class FileDbHelper { +public class FileDatabaseHelper { - private static final String TAG = FileDbHelper.class.getName(); + private static final String TAG = FileDatabaseHelper.class.getName(); - public static final String LAST_FILENAME = "lastFile"; - public static final String LAST_KEYFILE = "lastKey"; + static final String LAST_FILENAME = "lastFile"; + static final String LAST_KEYFILE = "lastKey"; public static final String DATABASE_NAME = "keepassdroid"; // TODO Change db name - private static final String FILE_TABLE = "files"; - private static final int DATABASE_VERSION = 1; + static final String FILE_TABLE = "files"; + static final int DATABASE_VERSION = 1; public static final int MAX_FILES = 5; @@ -49,74 +46,20 @@ public class FileDbHelper { public static final String KEY_FILE_KEYFILE = "keyFile"; public static final String KEY_FILE_UPDATED = "updated"; - private static final String DATABASE_CREATE = + static final String DATABASE_CREATE = "create table " + FILE_TABLE + " ( " + KEY_FILE_ID + " integer primary key autoincrement, " + KEY_FILE_FILENAME + " text not null, " + KEY_FILE_KEYFILE + " text, " + KEY_FILE_UPDATED + " integer not null);"; private final Context mCtx; - private DatabaseHelper mDbHelper; private SQLiteDatabase mDb; - private static class DatabaseHelper extends SQLiteOpenHelper { - private final Context mCtx; - - DatabaseHelper(Context ctx) { - super(ctx, DATABASE_NAME, null, DATABASE_VERSION); - mCtx = ctx; - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL(DATABASE_CREATE); - - // Migrate preference to database if it is set. - SharedPreferences settings = mCtx.getSharedPreferences("PasswordActivity", Context.MODE_PRIVATE); - String lastFile = settings.getString(LAST_FILENAME, ""); - String lastKey = settings.getString(LAST_KEYFILE,""); - - if ( lastFile.length() > 0 ) { - ContentValues vals = new ContentValues(); - vals.put(KEY_FILE_FILENAME, lastFile); - vals.put(KEY_FILE_UPDATED, System.currentTimeMillis()); - - if ( lastKey.length() > 0 ) { - vals.put(KEY_FILE_KEYFILE, lastKey); - } - - db.insert(FILE_TABLE, null, vals); - - // Clear old preferences - deletePrefs(settings); - - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // Only one database version so far - } - - private void deletePrefs(SharedPreferences prefs) { - // We won't worry too much if this fails - try { - SharedPreferences.Editor editor = prefs.edit(); - editor.remove(LAST_FILENAME); - editor.remove(LAST_KEYFILE); - editor.apply(); - } catch (Exception e) { - Log.e(TAG, "Unable to delete database preference", e); - } - } - } - - public FileDbHelper(Context ctx) { + public FileDatabaseHelper(Context ctx) { mCtx = ctx; } - public FileDbHelper open() throws SQLException { - mDbHelper = new DatabaseHelper(mCtx); - mDb = mDbHelper.getWritableDatabase(); + public FileDatabaseHelper open() throws SQLException { + mDb = new FileDatabaseHistoryHelper(mCtx).getWritableDatabase(); return this; } @@ -136,7 +79,6 @@ public class FileDbHelper { cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_ID}, KEY_FILE_FILENAME + "=?", new String[] {fileName}, null, null, null, null); } catch (Exception e ) { - assert(true); return -1; } @@ -167,7 +109,6 @@ public class FileDbHelper { deleteAllBut(MAX_FILES); } catch (Exception e) { e.printStackTrace(); - assert(true); } cursor.close(); diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt new file mode 100644 index 000000000..b6126e53d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt @@ -0,0 +1,259 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.fileselect.database + +import android.content.Context +import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener +import android.net.Uri +import android.preference.PreferenceManager + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.utils.SingletonHolder +import com.kunzisoft.keepass.utils.UriUtil +import java.lang.ref.WeakReference + +import java.util.ArrayList + +class FileDatabaseHistory private constructor(private val context: WeakReference) { + + private val mDatabasesUriList = ArrayList() + private val mKeyFilesUriList = ArrayList() + + private val mPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.get()) + var isEnabled: Boolean = false + + val databaseUriList: List + get() { + init() + return mDatabasesUriList + } + + private val onSharedPreferenceChangeListener = OnSharedPreferenceChangeListener { sharedPreferences, key -> + if (key == context.get()?.getString(R.string.recentfile_key)) { + isEnabled = sharedPreferences.getBoolean( + context.get()?.getString(R.string.recentfile_key), + context.get()?.resources?.getBoolean(R.bool.recentfile_default) ?: isEnabled) + } + } + + init { + mPreferences.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener) + context.get()?.resources?.let { + isEnabled = mPreferences.getBoolean( + it.getString(R.string.recentfile_key), + it.getBoolean(R.bool.recentfile_default)) + } + mPreferences.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener) + } + + private var init = false + @Synchronized + private fun init() { + if (!init) { + if (!upgradeFromSQL()) { + loadPrefs() + } + + init = true + } + } + + private fun upgradeFromSQL(): Boolean { + + try { + // Check for a database to upgrade from + if (context.get()?.getDatabasePath(FileDatabaseHelper.DATABASE_NAME)?.exists() != true) { + return false + } + + mDatabasesUriList.clear() + mKeyFilesUriList.clear() + + val helper = FileDatabaseHelper(context.get()) + helper.open() + val cursor = helper.fetchAllFiles() + + val dbIndex = cursor.getColumnIndex(FileDatabaseHelper.KEY_FILE_FILENAME) + val keyIndex = cursor.getColumnIndex(FileDatabaseHelper.KEY_FILE_KEYFILE) + + if (cursor.moveToFirst()) { + while (cursor.moveToNext()) { + mDatabasesUriList.add(cursor.getString(dbIndex)) + mKeyFilesUriList.add(cursor.getString(keyIndex)) + } + } + + savePrefs() + + cursor.close() + helper.close() + + } catch (e: Exception) { + // If upgrading fails, we'll just give up on it. + } + + try { + FileDatabaseHelper.deleteDatabase(context.get()) + } catch (e: Exception) { + // If we fail to delete it, just move on + } + + return true + } + + @JvmOverloads + fun addDatabaseUri(databaseUri: Uri?, keyFileUri: Uri? = null) { + if (!isEnabled || databaseUri == null) return + + init() + + // Remove any existing instance of the same filename + deleteDatabaseUri(databaseUri, false) + + mDatabasesUriList.add(0, databaseUri.toString()) + + val key = keyFileUri?.toString() ?: "" + mKeyFilesUriList.add(0, key) + + trimLists() + savePrefs() + } + + fun hasRecentFiles(): Boolean { + if (!isEnabled) return false + + init() + + return mDatabasesUriList.size > 0 + } + + fun getDatabaseAt(i: Int): String { + init() + return mDatabasesUriList[i] + } + + fun getKeyFileAt(i: Int): String { + init() + return mKeyFilesUriList[i] + } + + private fun loadPrefs() { + loadList(DB_KEY, mDatabasesUriList) + loadList(KEY_FILE_KEY, mKeyFilesUriList) + } + + private fun savePrefs() { + saveList(DB_KEY, mDatabasesUriList) + saveList(KEY_FILE_KEY, mKeyFilesUriList) + } + + private fun loadList(keyPrefix: String, list: MutableList) { + val size = mPreferences.getInt(keyPrefix, 0) + + list.clear() + for (i in 0 until size) { + list.add(mPreferences.getString(keyPrefix + "_" + i, "")) + } + } + + private fun saveList(keyPrefix: String, list: List) { + val edit = mPreferences.edit() + val size = list.size + edit.putInt(keyPrefix, size) + + for (i in 0 until size) { + edit.putString(keyPrefix + "_" + i, list[i]) + } + edit.apply() + } + + @JvmOverloads + fun deleteDatabaseUri(uri: Uri, save: Boolean = true) { + init() + + val uriName = uri.toString() + val fileName = uri.path + + for (i in mDatabasesUriList.indices) { + val entry = mDatabasesUriList[i] + if (uriName == entry || fileName == entry) { + mDatabasesUriList.removeAt(i) + mKeyFilesUriList.removeAt(i) + break + } + } + + if (save) { + savePrefs() + } + } + + fun getKeyFileUriByDatabaseUri(database: Uri): Uri? { + if (!isEnabled) return null + + init() + + val size = mDatabasesUriList.size + for (i in 0 until size) { + if (UriUtil.equalsDefaultfile(database, mDatabasesUriList[i])) { + return UriUtil.parseDefaultFile(mKeyFilesUriList[i]) + } + } + + return null + } + + fun deleteAll() { + init() + + mDatabasesUriList.clear() + mKeyFilesUriList.clear() + + savePrefs() + } + + fun deleteAllKeys() { + init() + + mKeyFilesUriList.clear() + + val size = mDatabasesUriList.size + for (i in 0 until size) { + mKeyFilesUriList.add("") + } + + savePrefs() + } + + private fun trimLists() { + val size = mDatabasesUriList.size + for (i in FileDatabaseHelper.MAX_FILES until size) { + mDatabasesUriList.removeAt(i) + mKeyFilesUriList.removeAt(i) + } + } + + companion object : SingletonHolder>(::FileDatabaseHistory) { + + private const val DB_KEY = "recent_databases" + private const val KEY_FILE_KEY = "recent_keyfiles" + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistoryHelper.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistoryHelper.java new file mode 100644 index 000000000..cae05e737 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistoryHelper.java @@ -0,0 +1,58 @@ +package com.kunzisoft.keepass.fileselect.database; + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +class FileDatabaseHistoryHelper extends SQLiteOpenHelper { + private final SharedPreferences settings; + + FileDatabaseHistoryHelper(Context context) { + super(context, FileDatabaseHelper.DATABASE_NAME, null, FileDatabaseHelper.DATABASE_VERSION); + settings = context.getSharedPreferences("PasswordActivity", Context.MODE_PRIVATE); + } + + @Override + public void onCreate(SQLiteDatabase sqLiteDatabase) { + sqLiteDatabase.execSQL(FileDatabaseHelper.DATABASE_CREATE); + + // Migrate preference to database if it is set. + String lastFile = settings.getString(FileDatabaseHelper.LAST_FILENAME, ""); + String lastKey = settings.getString(FileDatabaseHelper.LAST_KEYFILE,""); + + if ( lastFile.length() > 0 ) { + ContentValues contentValues = new ContentValues(); + contentValues.put(FileDatabaseHelper.KEY_FILE_FILENAME, lastFile); + contentValues.put(FileDatabaseHelper.KEY_FILE_UPDATED, System.currentTimeMillis()); + + if ( lastKey.length() > 0 ) { + contentValues.put(FileDatabaseHelper.KEY_FILE_KEYFILE, lastKey); + } + + sqLiteDatabase.insert(FileDatabaseHelper.FILE_TABLE, null, contentValues); + + // Clear old preferences + deletePrefs(settings); + } + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // Only one database version so far + } + + private void deletePrefs(SharedPreferences prefs) { + // We won't worry too much if this fails + try { + SharedPreferences.Editor editor = prefs.edit(); + editor.remove(FileDatabaseHelper.LAST_FILENAME); + editor.remove(FileDatabaseHelper.LAST_KEYFILE); + editor.apply(); + } catch (Exception e) { + Log.e(FileDatabaseHelper.class.getName(), "Unable to delete database preference", e); + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.java b/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.java index 32967bea5..72ee9435f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.java +++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconPackChooser.java @@ -108,7 +108,7 @@ public class IconPackChooser { public static void setSelectedIconPack(String iconPackIdString) { for(IconPack iconPack : iconPackList) { if (iconPack.getId().equals(iconPackIdString)) { - App.getDB().getDrawFactory().clearCache(); + App.Companion.getCurrentDatabase().getDrawFactory().clearCache(); iconPackSelected = iconPack; break; } diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt index 2d3b87308..875509217 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt @@ -4,7 +4,7 @@ import android.os.Bundle import android.support.v7.app.AppCompatActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.fileselect.FileSelectActivity +import com.kunzisoft.keepass.fileselect.FileDatabaseSelectActivity import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper @@ -15,11 +15,11 @@ class KeyboardLauncherActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - if (App.getDB().loaded && TimeoutHelper.checkTime(this)) + if (App.currentDatabase.loaded && TimeoutHelper.checkTime(this)) GroupActivity.launchForKeyboardSelection(this, PreferencesUtil.enableReadOnlyDatabase(this)) else { // Pass extra to get entry - FileSelectActivity.launchForKeyboardSelection(this) + FileDatabaseSelectActivity.launchForKeyboardSelection(this) } finish() super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 54e222f87..4bcc6c778 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -28,7 +28,6 @@ import android.content.SharedPreferences; import android.graphics.Color; import android.hardware.fingerprint.FingerprintManager; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -56,7 +55,6 @@ import com.kunzisoft.keepass.activities.ReadOnlyHelper; import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.app.App; import com.kunzisoft.keepass.autofill.AutofillHelper; -import com.kunzisoft.keepass.compat.ClipDataCompat; import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; import com.kunzisoft.keepass.database.element.Database; @@ -77,6 +75,7 @@ import permissions.dispatcher.*; import java.io.File; import java.io.FileNotFoundException; +import java.lang.ref.WeakReference; @RuntimePermissions public class PasswordActivity extends StylishActivity @@ -282,7 +281,7 @@ public class PasswordActivity extends StylishActivity protected void onResume() { // If the database isn't accessible make sure to clear the password field, if it // was saved in the instance state - if (App.getDB().getLoaded()) { + if (App.Companion.getCurrentDatabase().getLoaded()) { setEmptyViews(); } @@ -316,7 +315,7 @@ public class PasswordActivity extends StylishActivity checkboxPasswordView.setOnCheckedChangeListener(enableButtonOncheckedChangeListener); } - new UriIntentInitTask(this, mRememberKeyfile) + new UriIntentInitTask(new WeakReference<>(this), this, mRememberKeyfile) .execute(getIntent()); } @@ -868,7 +867,7 @@ public class PasswordActivity extends StylishActivity private void loadDatabase(String password, Uri keyfile) { // Clear before we load - Database database = App.getDB(); + Database database = App.Companion.getCurrentDatabase(); database.closeAndClear(getApplicationContext()); // Show the progress dialog and load the database @@ -876,7 +875,7 @@ public class PasswordActivity extends StylishActivity this, R.string.loading_database, progressTaskUpdater -> new LoadDatabaseRunnable( - PasswordActivity.this, + new WeakReference<>(PasswordActivity.this), database, mDbUri, password, @@ -1030,88 +1029,12 @@ public class PasswordActivity extends StylishActivity case LockingActivity.RESULT_EXIT_LOCK: case Activity.RESULT_CANCELED: setEmptyViews(); - App.getDB().closeAndClear(getApplicationContext()); + App.Companion.getCurrentDatabase().closeAndClear(getApplicationContext()); break; } } } - private static class UriIntentInitTask extends AsyncTask { - - static final String KEY_FILENAME = "fileName"; - static final String KEY_KEYFILE = "keyFile"; - private static final String VIEW_INTENT = "android.intent.action.VIEW"; - - private UriIntentInitTaskCallback uriIntentInitTaskCallback; - private boolean isKeyFileNeeded; - private Uri databaseUri; - private Uri keyFileUri; - - UriIntentInitTask(UriIntentInitTaskCallback uriIntentInitTaskCallback, boolean isKeyFileNeeded) { - this.uriIntentInitTaskCallback = uriIntentInitTaskCallback; - this.isKeyFileNeeded = isKeyFileNeeded; - } - - @Override - protected Integer doInBackground(Intent... args) { - Intent intent = args[0]; - String action = intent.getAction(); - if (action != null && action.equals(VIEW_INTENT)) { - Uri incoming = intent.getData(); - databaseUri = incoming; - keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE); - - if (incoming == null) { - return R.string.error_can_not_handle_uri; - } else if (incoming.getScheme().equals("file")) { - String fileName = incoming.getPath(); - - if (fileName.length() == 0) { - // No file name - return R.string.file_not_found; - } - - File dbFile = new File(fileName); - if (!dbFile.exists()) { - // File does not exist - return R.string.file_not_found; - } - - if (keyFileUri == null) { - keyFileUri = getKeyFile(databaseUri); - } - } else if (incoming.getScheme().equals("content")) { - if (keyFileUri == null) { - keyFileUri = getKeyFile(databaseUri); - } - } else { - return R.string.error_can_not_handle_uri; - } - - } else { - databaseUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_FILENAME)); - keyFileUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_KEYFILE)); - - if (keyFileUri == null || keyFileUri.toString().length() == 0) { - keyFileUri = getKeyFile(databaseUri); - } - } - return null; - } - - public void onPostExecute(Integer result) { - uriIntentInitTaskCallback.onPostInitTask(databaseUri, keyFileUri, result); - } - - private Uri getKeyFile(Uri dbUri) { - if (isKeyFileNeeded) { - return App.getFileHistory().getFileByName(dbUri); - } else { - return null; - } - } - } - @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) public void doNothing() {} diff --git a/app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTask.kt b/app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTask.kt new file mode 100644 index 000000000..c82b8a500 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTask.kt @@ -0,0 +1,89 @@ +package com.kunzisoft.keepass.password + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.AsyncTask + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.compat.ClipDataCompat +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory +import com.kunzisoft.keepass.utils.UriUtil + +import java.io.File +import java.lang.ref.WeakReference + +internal class UriIntentInitTask(private val weakContext: WeakReference, + private val uriIntentInitTaskCallback: UriIntentInitTaskCallback, + private val isKeyFileNeeded: Boolean) + : AsyncTask() { + + private var databaseUri: Uri? = null + private var keyFileUri: Uri? = null + + override fun doInBackground(vararg args: Intent): Int? { + val intent = args[0] + val action = intent.action + + // If is a view intent + if (action != null && action == VIEW_INTENT) { + val incoming = intent.data + databaseUri = incoming + keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE) + + if (incoming == null) { + return R.string.error_can_not_handle_uri + } else if (incoming.scheme == "file") { + val fileName = incoming.path + + if (fileName!!.isEmpty()) { + // No file name + return R.string.file_not_found + } + + val dbFile = File(fileName) + if (!dbFile.exists()) { + // File does not exist + return R.string.file_not_found + } + + if (keyFileUri == null) { + keyFileUri = getKeyFileUri(databaseUri) + } + } else if (incoming.scheme == "content") { + if (keyFileUri == null) { + keyFileUri = getKeyFileUri(databaseUri) + } + } else { + return R.string.error_can_not_handle_uri + } + + } else { + databaseUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_FILENAME)) + keyFileUri = UriUtil.parseDefaultFile(intent.getStringExtra(KEY_KEYFILE)) + + if (keyFileUri == null || keyFileUri!!.toString().isEmpty()) { + keyFileUri = getKeyFileUri(databaseUri) + } + } + return null + } + + public override fun onPostExecute(result: Int?) { + uriIntentInitTaskCallback.onPostInitTask(databaseUri, keyFileUri, result) + } + + private fun getKeyFileUri(databaseUri: Uri?): Uri? { + return if (isKeyFileNeeded) { + FileDatabaseHistory.getInstance(weakContext).getKeyFileUriByDatabaseUri(databaseUri!!) + } else { + null + } + } + + companion object { + const val KEY_FILENAME = "fileName" + const val KEY_KEYFILE = "keyFile" + private const val VIEW_INTENT = "android.intent.action.VIEW" + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java index 76514ef49..82cc8743b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.java @@ -59,7 +59,7 @@ public class MainPreferenceFragment extends PreferenceFragmentCompat implements preference = findPreference(getString(R.string.db_key)); preference.setOnPreferenceClickListener(this); - Database db = App.getDB(); + Database db = App.Companion.getCurrentDatabase(); if (!(db.getLoaded())) { preference.setEnabled(false); } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index d7fd7dfd6..e9e4cea73 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -49,6 +49,7 @@ import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.dialogs.ProFeatureDialogFragment; import com.kunzisoft.keepass.dialogs.UnavailableFeatureDialogFragment; import com.kunzisoft.keepass.dialogs.UnderDevelopmentFeatureDialogFragment; +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; import com.kunzisoft.keepass.icons.IconPackChooser; import com.kunzisoft.keepass.dialogs.KeyboardExplanationDialogFragment; @@ -61,6 +62,8 @@ import com.kunzisoft.keepass.settings.preferencedialogfragment.ParallelismPrefer import com.kunzisoft.keepass.settings.preferencedialogfragment.RoundsPreferenceDialogFragmentCompat; import com.kunzisoft.keepass.stylish.Stylish; +import java.lang.ref.WeakReference; + public class NestedSettingsFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener { @@ -123,7 +126,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat if (getArguments() != null) key = getArguments().getInt(TAG_KEY); - database = App.getDB(); + database = App.Companion.getCurrentDatabase(); databaseReadOnly = ReadOnlyHelper.INSTANCE.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, getArguments()); databaseReadOnly = database.isReadOnly() || databaseReadOnly; @@ -138,7 +141,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat keyFile.setOnPreferenceChangeListener((preference, newValue) -> { Boolean value = (Boolean) newValue; if (!value) { - App.getFileHistory().deleteAllKeys(); + FileDatabaseHistory.Companion.getInstance(new WeakReference<>(getContext().getApplicationContext())).deleteAllKeys(); } return true; }); @@ -150,7 +153,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat value = true; } if (!value) { - App.getFileHistory().deleteAll(); + FileDatabaseHistory.Companion.getInstance(new WeakReference<>(getContext().getApplicationContext())).deleteAll(); } return true; }); diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt index 234016aca..d97b5b233 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt @@ -34,7 +34,7 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialo override fun onBindDialogView(view: View) { super.onBindDialogView(view) - this.database = App.getDB() + this.database = App.currentDatabase } override fun onDialogClosed(positiveResult: Boolean) { diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index 9227aea1b..61df440c6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -62,7 +62,7 @@ object TimeoutHelper { edit.putLong(context.getString(R.string.timeout_backup_key), time) edit.apply() - if (App.getDB().loaded) { + if (App.currentDatabase.loaded) { val timeout = try { java.lang.Long.parseLong(prefs.getString(context.getString(R.string.app_timeout_key), context.getString(R.string.clipboard_timeout_default))) @@ -91,7 +91,7 @@ object TimeoutHelper { return true // Cancel the lock PendingIntent - if (App.getDB().loaded) { + if (App.currentDatabase.loaded) { val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager Log.d(TAG, "TimeoutHelper cancel") am.cancel(getLockPendingIntent(context)) diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/SingletonHolder.kt b/app/src/main/java/com/kunzisoft/keepass/utils/SingletonHolder.kt new file mode 100644 index 000000000..f0ab79c8c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/utils/SingletonHolder.kt @@ -0,0 +1,17 @@ +package com.kunzisoft.keepass.utils + +open class SingletonHolder(private val constructor: (A) -> T) { + + @Volatile + private var instance: T? = null + + fun getInstance(arg: A): T { + return when { + instance != null -> instance!! + else -> synchronized(this) { + if (instance == null) instance = constructor(arg) + instance!! + } + } + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 5a4e1dae0..b3c7a0330 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -org.gradle.jvmargs=-Xmx4608M +org.gradle.jvmargs=-Xmx2048M From 77c397e0d110b776d7dfa6d81a78d17c60d594ca Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 4 Jul 2019 19:10:33 +0200 Subject: [PATCH 131/289] Fix small bugs --- .../keepass/activities/AboutActivity.java | 88 ------------------- .../keepass/activities/AboutActivity.kt | 82 +++++++++++++++++ .../FileDatabaseSelectActivity.java | 2 +- 3 files changed, 83 insertions(+), 89 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.java deleted file mode 100644 index 2a2d41516..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.activities; - -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.MenuItem; -import android.widget.TextView; - -import com.kunzisoft.keepass.BuildConfig; -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.stylish.StylishActivity; - -import org.joda.time.DateTime; - -public class AboutActivity extends StylishActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.about); - - Toolbar toolbar = findViewById(R.id.toolbar); - toolbar.setTitle(getString(R.string.menu_about)); - setSupportActionBar(toolbar); - assert getSupportActionBar() != null; - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - - String version; - String build; - try { - version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName; - build = BuildConfig.BUILD_VERSION; - } catch (NameNotFoundException e) { - Log.w(getClass().getSimpleName(), "Unable to get the app or the build version", e); - version = "Unable to get the app version"; - build = "Unable to get the build version"; - } - version = getString(R.string.version_label, version); - TextView versionTextView = findViewById(R.id.activity_about_version); - versionTextView.setText(version); - - build = getString(R.string.build_label, build); - TextView buildTextView = findViewById(R.id.activity_about_build); - buildTextView.setText(build); - - - TextView disclaimerText = findViewById(R.id.disclaimer); - disclaimerText.setText(getString(R.string.disclaimer_formal, new DateTime().getYear())); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - switch (id) { - case android.R.id.home: - finish(); - break; - } - return super.onOptionsItemSelected(item); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt new file mode 100644 index 000000000..c2018c06c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.activities + +import android.content.pm.PackageManager.NameNotFoundException +import android.os.Bundle +import android.support.v7.widget.Toolbar +import android.util.Log +import android.view.MenuItem +import android.widget.TextView + +import com.kunzisoft.keepass.BuildConfig +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.stylish.StylishActivity + +import org.joda.time.DateTime + +class AboutActivity : StylishActivity() { + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.about) + + val toolbar = findViewById(R.id.toolbar) + toolbar.title = getString(R.string.menu_about) + setSupportActionBar(toolbar) + assert(supportActionBar != null) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + supportActionBar!!.setDisplayShowHomeEnabled(true) + + var version: String + var build: String + try { + version = packageManager.getPackageInfo(packageName, 0).versionName + build = BuildConfig.BUILD_VERSION + } catch (e: NameNotFoundException) { + Log.w(javaClass.simpleName, "Unable to get the app or the build version", e) + version = "Unable to get the app version" + build = "Unable to get the build version" + } + + version = getString(R.string.version_label, version) + val versionTextView = findViewById(R.id.activity_about_version) + versionTextView.text = version + + build = getString(R.string.build_label, build) + val buildTextView = findViewById(R.id.activity_about_build) + buildTextView.text = build + + + val disclaimerText = findViewById(R.id.disclaimer) + disclaimerText.text = getString(R.string.disclaimer_formal, DateTime().year) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + when (item.itemId) { + android.R.id.home -> finish() + } + return super.onOptionsItemSelected(item) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java index 80f0b323a..1ee943508 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java @@ -91,7 +91,7 @@ public class FileDatabaseSelectActivity extends StylishActivity implements FileDatabaseHistoryAdapter.FileSelectClearListener, FileDatabaseHistoryAdapter.FileInformationShowListener { - private static final String TAG = "FileDatabaseSelectActivity"; + private static final String TAG = "FileDbSelectActivity"; private static final String EXTRA_STAY = "EXTRA_STAY"; From e8b341c69f147f9c8eafb8843fb634c02ddf66cb Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 15:23:47 +0200 Subject: [PATCH 132/289] Refactor Education --- .../keepass/activities/EntryActivity.java | 115 +++---- .../keepass/activities/EntryEditActivity.java | 118 ++++---- .../keepass/activities/GroupActivity.java | 199 ++++--------- .../kunzisoft/keepass/education/Education.kt | 281 ++++++++++++++++++ .../education/EntryActivityEducation.kt | 69 +++++ .../education/EntryEditActivityEducation.kt | 66 ++++ .../FileDatabaseSelectActivityEducation.kt | 102 +++++++ .../education/GroupActivityEducation.kt | 112 +++++++ .../education/PasswordActivityEducation.kt | 90 ++++++ .../FileDatabaseSelectActivity.java | 153 +++------- .../keepass/password/PasswordActivity.java | 166 ++++------- .../settings/NestedSettingsFragment.java | 11 +- .../keepass/settings/PreferencesUtil.java | 215 -------------- app/src/main/res/values/donottranslate.xml | 2 + 14 files changed, 968 insertions(+), 731 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/education/Education.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/education/EntryActivityEducation.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/education/EntryEditActivityEducation.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/education/FileDatabaseSelectActivityEducation.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/education/GroupActivityEducation.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/education/PasswordActivityEducation.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java index 09f2dfcb8..09a27ea44 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java @@ -37,8 +37,7 @@ import android.view.MenuItem; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; -import com.getkeepsafe.taptargetview.TapTarget; -import com.getkeepsafe.taptargetview.TapTargetView; + import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; @@ -46,6 +45,7 @@ import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.database.element.EntryVersioned; import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.security.ProtectedString; +import com.kunzisoft.keepass.education.EntryActivityEducation; import com.kunzisoft.keepass.notifications.NotificationCopyingService; import com.kunzisoft.keepass.notifications.NotificationField; import com.kunzisoft.keepass.settings.PreferencesUtil; @@ -240,79 +240,6 @@ public class EntryActivity extends LockingHideActivity { firstLaunchOfActivity = false; } - /** - * Check and display learning views - * Displays the explanation for copying a field and editing an entry - */ - private void checkAndPerformedEducation(Menu menu) { - if (PreferencesUtil.isEducationScreensEnabled(this)) { - - if (entryContentsView != null && entryContentsView.isUserNamePresent() - && !PreferencesUtil.isEducationCopyUsernamePerformed(this)) { - TapTargetView.showFor(this, - TapTarget.forView(findViewById(R.id.entry_user_name_action_image), - getString(R.string.education_field_copy_title), - getString(R.string.education_field_copy_summary)) - .textColorInt(Color.WHITE) - .tintTarget(false) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - clipboardHelper.timeoutCopyToClipboard(mEntry.getUsername(), - getString(R.string.copy_field, getString(R.string.entry_user_name))); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - // Launch autofill settings - startActivity(new Intent(EntryActivity.this, SettingsAutofillActivity.class)); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_copy_username_key); - - } else if (!PreferencesUtil.isEducationEntryEditPerformed(this)) { - - try { - TapTargetView.showFor(this, - TapTarget.forToolbarMenuItem(toolbar, R.id.menu_edit, - getString(R.string.education_entry_edit_title), - getString(R.string.education_entry_edit_summary)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - MenuItem editItem = menu.findItem(R.id.menu_edit); - onOptionsItemSelected(editItem); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - // Open Keepass doc to create field references - Intent browserIntent = new Intent(Intent.ACTION_VIEW, - Uri.parse(getString(R.string.field_references_url))); - startActivity(browserIntent); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_entry_edit_key); - } catch (Exception e) { - // If icon not visible - Log.w(TAG, "Can't performed education for entry's edition"); - } - } - } - } - protected void fillData() { Database database = App.Companion.getCurrentDatabase(); database.startManageEntry(mEntry); @@ -460,11 +387,45 @@ public class EntryActivity extends LockingHideActivity { } // Show education views - new Handler().post(() -> checkAndPerformedEducation(menu)); - + new Handler().post(() -> performedNextEducation(new EntryActivityEducation(this), menu)); + return true; } + private void performedNextEducation(EntryActivityEducation entryActivityEducation, + Menu menu) { + if (entryContentsView != null + && entryContentsView.isUserNamePresent() + && entryActivityEducation.checkAndPerformedEntryCopyEducation( + findViewById(R.id.entry_user_name_action_image), + tapTargetView -> { + clipboardHelper.timeoutCopyToClipboard(mEntry.getUsername(), + getString(R.string.copy_field, + getString(R.string.entry_user_name))); + return null; + }, + tapTargetView -> { + // Launch autofill settings + startActivity(new Intent(EntryActivity.this, SettingsAutofillActivity.class)); + return null; + }) + ); + else if (toolbar.findViewById(R.id.menu_edit) != null + && entryActivityEducation.checkAndPerformedEntryEditEducation( + toolbar.findViewById(R.id.menu_edit), + tapTargetView -> { + onOptionsItemSelected(menu.findItem(R.id.menu_edit)); + return null; + }, + tapTargetView -> { + // Open Keepass doc to create field references + startActivity(new Intent(Intent.ACTION_VIEW, + Uri.parse(getString(R.string.field_references_url)))); + return null; + }) + ); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch ( item.getItemId() ) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java index bffdcffc8..5ee760dcb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java @@ -24,12 +24,20 @@ import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Bundle; +import android.os.Handler; import android.support.v7.widget.Toolbar; import android.util.Log; -import android.view.*; -import android.widget.*; -import com.getkeepsafe.taptargetview.TapTarget; -import com.getkeepsafe.taptargetview.TapTargetView; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.Toast; + import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.lock.LockingHideActivity; import com.kunzisoft.keepass.app.App; @@ -37,16 +45,24 @@ import com.kunzisoft.keepass.database.action.node.ActionNodeValues; import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; -import com.kunzisoft.keepass.database.element.*; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.EntryVersioned; +import com.kunzisoft.keepass.database.element.GroupVersioned; +import com.kunzisoft.keepass.database.element.PwDate; +import com.kunzisoft.keepass.database.element.PwIcon; +import com.kunzisoft.keepass.database.element.PwIconStandard; +import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; +import com.kunzisoft.keepass.education.EntryEditActivityEducation; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.timeout.TimeoutHelper; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.Util; import com.kunzisoft.keepass.view.EntryEditCustomField; + import org.jetbrains.annotations.NotNull; import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD; @@ -90,6 +106,9 @@ public class EntryEditActivity extends LockingHideActivity private View saveView; private int iconColor; + // Education + private EntryEditActivityEducation entryEditActivityEducation; + /** * Launch EntryEditActivity to update an existing entry * @@ -218,8 +237,33 @@ public class EntryEditActivity extends LockingHideActivity } // Verify the education views - checkAndPerformedEducation(); - } + entryEditActivityEducation = new EntryEditActivityEducation(this); + new Handler().post(() -> performedNextEducation(entryEditActivityEducation)); + } + + private void performedNextEducation(EntryEditActivityEducation entryEditActivityEducation) { + if (entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation( + generatePasswordView, + tapTargetView -> { + openPasswordGenerator(); + return null; + }, + tapTargetView -> { + performedNextEducation(entryEditActivityEducation); + return null; + } + )); + else if (mEntry.allowExtraFields() + && !mEntry.containsCustomFields() + && entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation( + addNewFieldView, + tapTargetView -> { + addNewCustomField(); + return null; + }, + tapTargetView -> null) + ); + } /** * Open the password generator fragment @@ -283,65 +327,7 @@ public class EntryEditActivity extends LockingHideActivity new Thread(task).start(); } - /** - * Check and display learning views - * Displays the explanation for the icon selection, the password generator and for a new field - */ - private void checkAndPerformedEducation() { - if (PreferencesUtil.isEducationScreensEnabled(this)) { - // TODO Show icon - if (!PreferencesUtil.isEducationPasswordGeneratorPerformed(this)) { - TapTargetView.showFor(this, - TapTarget.forView(generatePasswordView, - getString(R.string.education_generate_password_title), - getString(R.string.education_generate_password_summary)) - .textColorInt(Color.WHITE) - .tintTarget(false) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - openPasswordGenerator(); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_password_generator_key); - } else if (mEntry.allowExtraFields() - && !mEntry.containsCustomFields() - && !PreferencesUtil.isEducationEntryNewFieldPerformed(this)) { - TapTargetView.showFor(this, - TapTarget.forView(addNewFieldView, - getString(R.string.education_entry_new_field_title), - getString(R.string.education_entry_new_field_summary)) - .textColorInt(Color.WHITE) - .tintTarget(false) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - addNewCustomField(); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_entry_new_field_key); - } - } - } /** * Utility class to retrieve a validation or an error with a message @@ -549,7 +535,7 @@ public class EntryEditActivity extends LockingHideActivity entryPasswordView.setText(generatedPassword); entryConfirmationPasswordView.setText(generatedPassword); - checkAndPerformedEducation(); + new Handler().post(() -> performedNextEducation(entryEditActivityEducation)); } @Override diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 382afbf94..3b69e8688 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -51,8 +51,6 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import com.getkeepsafe.taptargetview.TapTarget; -import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.adapters.NodeAdapter; @@ -71,7 +69,12 @@ import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable; import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable; import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; -import com.kunzisoft.keepass.database.element.*; +import com.kunzisoft.keepass.database.element.Database; +import com.kunzisoft.keepass.database.element.EntryVersioned; +import com.kunzisoft.keepass.database.element.GroupVersioned; +import com.kunzisoft.keepass.database.element.NodeVersioned; +import com.kunzisoft.keepass.database.element.PwIcon; +import com.kunzisoft.keepass.database.element.PwNodeId; import com.kunzisoft.keepass.database.element.security.ProtectedString; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; @@ -79,6 +82,7 @@ import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; import com.kunzisoft.keepass.dialogs.ReadOnlyDialog; import com.kunzisoft.keepass.dialogs.SortDialogFragment; +import com.kunzisoft.keepass.education.GroupActivityEducation; import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.magikeyboard.MagikIME; @@ -712,141 +716,6 @@ public class GroupActivity extends LockingActivity searchSuggestionAdapter.reInit(this); } - /** - * Check and display learning views - * Displays the explanation for a add, search, sort a new node and lock the database - */ - private void checkAndPerformedEducation(Menu menu) { - if (PreferencesUtil.isEducationScreensEnabled(this)) { - - // If no node, show education to add new one - if (listNodesFragment != null - && listNodesFragment.isEmpty()) { - if (!PreferencesUtil.isEducationNewNodePerformed(this) - && addNodeButtonView.isEnable()) { - - TapTargetView.showFor(this, - TapTarget.forView(findViewById(R.id.add_button), - getString(R.string.education_new_node_title), - getString(R.string.education_new_node_summary)) - .textColorInt(Color.WHITE) - .tintTarget(false) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - addNodeButtonView.openButtonIfClose(); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_new_node_key); - - } - } - // Else show the search education - else if (!PreferencesUtil.isEducationSearchPerformed(this)) { - - try { - TapTargetView.showFor(this, - TapTarget.forToolbarMenuItem(toolbar, R.id.menu_search, - getString(R.string.education_search_title), - getString(R.string.education_search_summary)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - MenuItem searchItem = menu.findItem(R.id.menu_search); - searchItem.expandActionView(); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_search_key); - } catch (Exception e) { - // If icon not visible - Log.w(TAG, "Can't performed education for search"); - } - } - // Else show the sort education - else if (!PreferencesUtil.isEducationSortPerformed(this)) { - - try { - TapTargetView.showFor(this, - TapTarget.forToolbarMenuItem(toolbar, R.id.menu_sort, - getString(R.string.education_sort_title), - getString(R.string.education_sort_summary)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - MenuItem sortItem = menu.findItem(R.id.menu_sort); - onOptionsItemSelected(sortItem); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_sort_key); - } catch (Exception e) { - Log.w(TAG, "Can't performed education for sort"); - } - } - // Else show the lock education - else if (!PreferencesUtil.isEducationLockPerformed(this)) { - - try { - TapTargetView.showFor(this, - TapTarget.forToolbarMenuItem(toolbar, R.id.menu_lock, - getString(R.string.education_lock_title), - getString(R.string.education_lock_summary)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - MenuItem lockItem = menu.findItem(R.id.menu_lock); - onOptionsItemSelected(lockItem); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_lock_key); - } catch (Exception e) { - Log.w(TAG, "Can't performed education for lock"); - } - } - } - } - @Override protected void onStop() { super.onStop(); @@ -898,11 +767,63 @@ public class GroupActivity extends LockingActivity super.onCreateOptionsMenu(menu); // Launch education screen - new Handler().post(() -> checkAndPerformedEducation(menu)); + new Handler().post(() -> performedNextEducation(new GroupActivityEducation(this), menu)); return true; } + private void performedNextEducation(GroupActivityEducation groupActivityEducation, + Menu menu) { + // If no node, show education to add new one + if (listNodesFragment != null + && listNodesFragment.isEmpty() + && addNodeButtonView.isEnable() + && groupActivityEducation.checkAndPerformedAddNodeButtonEducation( + addNodeButtonView, + tapTargetView -> { + addNodeButtonView.openButtonIfClose(); + return null; + }, + tapTargetView -> { + performedNextEducation(groupActivityEducation, menu); + return null; + } + )); + else if (toolbar.findViewById(R.id.menu_search) != null + && groupActivityEducation.checkAndPerformedSearchMenuEducation( + toolbar.findViewById(R.id.menu_search), + tapTargetView -> { + menu.findItem(R.id.menu_search).expandActionView(); + return null; + }, + tapTargetView -> { + performedNextEducation(groupActivityEducation, menu); + return null; + })); + else if (toolbar.findViewById(R.id.menu_sort) != null + && groupActivityEducation.checkAndPerformedSortMenuEducation( + toolbar.findViewById(R.id.menu_sort), + tapTargetView -> { + onOptionsItemSelected(menu.findItem(R.id.menu_sort)); + return null; + }, + tapTargetView -> { + performedNextEducation(groupActivityEducation, menu); + return null; + })); + else if (toolbar.findViewById(R.id.menu_lock) != null + && groupActivityEducation.checkAndPerformedLockMenuEducation( + toolbar.findViewById(R.id.menu_lock), + tapTargetView -> { + onOptionsItemSelected(menu.findItem(R.id.menu_lock)); + return null; + }, + tapTargetView -> { + performedNextEducation(groupActivityEducation, menu); + return null; + })); + } + @Override public void startActivity(Intent intent) { diff --git a/app/src/main/java/com/kunzisoft/keepass/education/Education.kt b/app/src/main/java/com/kunzisoft/keepass/education/Education.kt new file mode 100644 index 000000000..d39783e2b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/education/Education.kt @@ -0,0 +1,281 @@ +package com.kunzisoft.keepass.education + +import android.app.Activity +import android.content.Context +import android.content.SharedPreferences +import android.preference.PreferenceManager +import android.util.Log +import com.getkeepsafe.taptargetview.TapTarget +import com.getkeepsafe.taptargetview.TapTargetView +import com.kunzisoft.keepass.R + +open class Education(val activity: Activity) { + + /** + * Utility method to save preference after an education action + */ + protected fun checkAndPerformedEducation(isEducationAlreadyPerformed: Boolean, + tapTarget: TapTarget, + listener: TapTargetView.Listener, + saveEducationStringId: Int): Boolean { + var doEducation = false + if (isEducationScreensEnabled()) { + if (isEducationAlreadyPerformed) { + try { + TapTargetView.showFor(activity, tapTarget, listener) + saveEducationPreference(activity, saveEducationStringId) + doEducation = true + } catch (e: Exception) { + Log.w(Education::class.java.name, "Can't performed education " + e.message) + } + } + } + return doEducation + } + + + /** + * Define if educations screens are enabled + */ + fun isEducationScreensEnabled(): Boolean { + return isEducationScreensEnabled(activity) + } + + /** + * Register education preferences as true in EDUCATION_PREFERENCE SharedPreferences + * + * @param context The context to retrieve the key string in XML + * @param educationKeys Keys to save as boolean 'true' + */ + fun saveEducationPreference(context: Context, vararg educationKeys: Int) { + val sharedPreferences = getEducationSharedPreferences(context) + val editor = sharedPreferences.edit() + for (key in educationKeys) { + editor.putBoolean(context.getString(key), true) + } + editor.apply() + } + + companion object { + + private const val EDUCATION_PREFERENCE = "kdbxeducation" + + /** + * All preference keys associated with education + */ + val educationResourcesKeys = intArrayOf( + R.string.education_create_db_key, + R.string.education_select_db_key, + R.string.education_open_link_db_key, + R.string.education_unlock_key, + R.string.education_read_only_key, + R.string.education_fingerprint_key, + R.string.education_search_key, + R.string.education_new_node_key, + R.string.education_sort_key, + R.string.education_lock_key, + R.string.education_copy_username_key, + R.string.education_entry_edit_key, + R.string.education_password_generator_key, + R.string.education_entry_new_field_key) + + + /** + * Get preferences bundle for education + */ + fun getEducationSharedPreferences(ctx: Context): SharedPreferences { + return ctx.getSharedPreferences( + EDUCATION_PREFERENCE, + Context.MODE_PRIVATE) + } + + /** + * Define if educations screens are enabled + */ + fun isEducationScreensEnabled(context: Context): Boolean { + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + return sharedPreferences.getBoolean(context.getString(R.string.enable_education_screens_key), + context.resources.getBoolean(R.bool.enable_education_screens_default)) + } + + /** + * Determines whether the explanatory view of the database creation has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_create_db_key key + */ + fun isEducationCreateDatabasePerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_create_db_key), + context.resources.getBoolean(R.bool.education_create_db_default)) + } + + /** + * Determines whether the explanatory view of the database selection has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_select_db_key key + */ + fun isEducationSelectDatabasePerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_select_db_key), + context.resources.getBoolean(R.bool.education_select_db_default)) + } + + /** + * Determines whether the explanatory view of the database selection has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_select_db_key key + */ + fun isEducationOpenLinkDatabasePerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_open_link_db_key), + context.resources.getBoolean(R.bool.education_open_link_db_default)) + } + + /** + * Determines whether the explanatory view of the database unlock has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_unlock_key key + */ + fun isEducationUnlockPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_unlock_key), + context.resources.getBoolean(R.bool.education_unlock_default)) + } + + /** + * Determines whether the explanatory view of the database read-only has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_read_only_key key + */ + fun isEducationReadOnlyPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_read_only_key), + context.resources.getBoolean(R.bool.education_read_only_default)) + } + + /** + * Determines whether the explanatory view of the fingerprint unlock has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_fingerprint_key key + */ + fun isEducationFingerprintPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_fingerprint_key), + context.resources.getBoolean(R.bool.education_fingerprint_default)) + } + + /** + * Determines whether the explanatory view of search has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_search_key key + */ + fun isEducationSearchPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_search_key), + context.resources.getBoolean(R.bool.education_search_default)) + } + + /** + * Determines whether the explanatory view of add new node has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_new_node_key key + */ + fun isEducationNewNodePerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_new_node_key), + context.resources.getBoolean(R.bool.education_new_node_default)) + } + + /** + * Determines whether the explanatory view of the sort has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_sort_key key + */ + fun isEducationSortPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_sort_key), + context.resources.getBoolean(R.bool.education_sort_default)) + } + + /** + * Determines whether the explanatory view of the database lock has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_lock_key key + */ + fun isEducationLockPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_lock_key), + context.resources.getBoolean(R.bool.education_lock_default)) + } + + /** + * Determines whether the explanatory view of the username copy has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_copy_username_key key + */ + fun isEducationCopyUsernamePerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_copy_username_key), + context.resources.getBoolean(R.bool.education_copy_username_key)) + } + + /** + * Determines whether the explanatory view of the entry edition has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_entry_edit_key key + */ + fun isEducationEntryEditPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_entry_edit_key), + context.resources.getBoolean(R.bool.education_entry_edit_default)) + } + + /** + * Determines whether the explanatory view of the password generator has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_password_generator_key key + */ + fun isEducationPasswordGeneratorPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_password_generator_key), + context.resources.getBoolean(R.bool.education_password_generator_default)) + } + + /** + * Determines whether the explanatory view of the new fields button in an entry has already been displayed. + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_entry_new_field_key key + */ + fun isEducationEntryNewFieldPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_entry_new_field_key), + context.resources.getBoolean(R.bool.education_entry_new_field_default)) + } + + /** + * Defines if the reset education preference has been reclicked + * + * @param context The context to open the SharedPreferences + * @return boolean value of education_screen_reclicked_key key + */ + fun isEducationScreenReclickedPerformed(context: Context): Boolean { + val prefs = getEducationSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.education_screen_reclicked_key), + context.resources.getBoolean(R.bool.education_screen_reclicked_default)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/education/EntryActivityEducation.kt b/app/src/main/java/com/kunzisoft/keepass/education/EntryActivityEducation.kt new file mode 100644 index 000000000..853e8b0e6 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/education/EntryActivityEducation.kt @@ -0,0 +1,69 @@ +package com.kunzisoft.keepass.education + +import android.app.Activity +import android.graphics.Color +import android.view.View +import com.getkeepsafe.taptargetview.TapTarget +import com.getkeepsafe.taptargetview.TapTargetView +import com.kunzisoft.keepass.R + + +class EntryActivityEducation(activity: Activity) + : Education(activity) { + + fun checkAndPerformedEntryCopyEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation( + !isEducationCopyUsernamePerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_field_copy_title), + activity.getString(R.string.education_field_copy_summary)) + .textColorInt(Color.WHITE) + .tintTarget(false) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_copy_username_key) + } + + /** + * Check and display learning views + * Displays the explanation for copying a field and editing an entry + */ + fun checkAndPerformedEntryEditEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation( + !isEducationEntryEditPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_entry_edit_title), + activity.getString(R.string.education_entry_edit_summary)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_entry_edit_key) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/education/EntryEditActivityEducation.kt b/app/src/main/java/com/kunzisoft/keepass/education/EntryEditActivityEducation.kt new file mode 100644 index 000000000..ad4190a7f --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/education/EntryEditActivityEducation.kt @@ -0,0 +1,66 @@ +package com.kunzisoft.keepass.education + +import android.app.Activity +import android.graphics.Color +import android.view.View +import com.getkeepsafe.taptargetview.TapTarget +import com.getkeepsafe.taptargetview.TapTargetView +import com.kunzisoft.keepass.R + +class EntryEditActivityEducation(activity: Activity) + : Education(activity) { + + fun checkAndPerformedGeneratePasswordEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationPasswordGeneratorPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_generate_password_title), + activity.getString(R.string.education_generate_password_summary)) + .textColorInt(Color.WHITE) + .tintTarget(false) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_password_generator_key) + } + + /** + * Check and display learning views + * Displays the explanation for the icon selection, the password generator and for a new field + */ + fun checkAndPerformedEntryNewFieldEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationEntryNewFieldPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_entry_new_field_title), + activity.getString(R.string.education_entry_new_field_summary)) + .textColorInt(Color.WHITE) + .tintTarget(false) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_entry_new_field_key) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/education/FileDatabaseSelectActivityEducation.kt b/app/src/main/java/com/kunzisoft/keepass/education/FileDatabaseSelectActivityEducation.kt new file mode 100644 index 000000000..975bbef0d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/education/FileDatabaseSelectActivityEducation.kt @@ -0,0 +1,102 @@ +package com.kunzisoft.keepass.education + +import android.app.Activity +import android.graphics.Color +import android.support.v4.content.ContextCompat +import android.view.View +import com.getkeepsafe.taptargetview.TapTarget +import com.getkeepsafe.taptargetview.TapTargetView +import com.kunzisoft.keepass.R + +class FileDatabaseSelectActivityEducation(activity: Activity) + : Education(activity) { + + /** + * Check and display learning views + * Displays the explanation for a database creation then a database selection + */ + fun checkAndPerformedCreateDatabaseEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + + // Try to open the creation base education + return checkAndPerformedEducation(!isEducationCreateDatabasePerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_create_database_title), + activity.getString(R.string.education_create_database_summary)) + .icon(ContextCompat.getDrawable(activity, R.drawable.ic_database_plus_white_24dp)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_create_db_key) + } + + /** + * Check and display learning views + * Displays the explanation for a database selection + */ + fun checkAndPerformedSelectDatabaseEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationSelectDatabasePerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_select_database_title), + activity.getString(R.string.education_select_database_summary)) + .icon(ContextCompat.getDrawable(activity, R.drawable.ic_folder_white_24dp)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_select_db_key) + } + + + fun checkAndPerformedOpenLinkDatabaseEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationOpenLinkDatabasePerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_open_link_database_title), + activity.getString(R.string.education_open_link_database_summary)) + .icon(ContextCompat.getDrawable(activity, R.drawable.ic_link_white_24dp)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_open_link_db_key) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/education/GroupActivityEducation.kt b/app/src/main/java/com/kunzisoft/keepass/education/GroupActivityEducation.kt new file mode 100644 index 000000000..e4cde8545 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/education/GroupActivityEducation.kt @@ -0,0 +1,112 @@ +package com.kunzisoft.keepass.education + +import android.app.Activity +import android.graphics.Color +import android.view.View +import com.getkeepsafe.taptargetview.TapTarget +import com.getkeepsafe.taptargetview.TapTargetView +import com.kunzisoft.keepass.R + +class GroupActivityEducation(activity: Activity) + : Education(activity) { + + fun checkAndPerformedAddNodeButtonEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationNewNodePerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_new_node_title), + activity.getString(R.string.education_new_node_summary)) + .textColorInt(Color.WHITE) + .tintTarget(false) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_new_node_key) + } + + fun checkAndPerformedSearchMenuEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationSearchPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_search_title), + activity.getString(R.string.education_search_summary)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_search_key) + } + + fun checkAndPerformedSortMenuEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationSortPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_sort_title), + activity.getString(R.string.education_sort_summary)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_sort_key) + } + + fun checkAndPerformedLockMenuEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationLockPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_lock_title), + activity.getString(R.string.education_lock_summary)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_lock_key) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/education/PasswordActivityEducation.kt b/app/src/main/java/com/kunzisoft/keepass/education/PasswordActivityEducation.kt new file mode 100644 index 000000000..ab425fe0c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/education/PasswordActivityEducation.kt @@ -0,0 +1,90 @@ +package com.kunzisoft.keepass.education + +import android.app.Activity +import android.graphics.Color +import android.support.v4.content.ContextCompat +import android.view.View +import com.getkeepsafe.taptargetview.TapTarget +import com.getkeepsafe.taptargetview.TapTargetView +import com.kunzisoft.keepass.R + +class PasswordActivityEducation(activity: Activity) + : Education(activity) { + + fun checkAndPerformedFingerprintUnlockEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationUnlockPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_unlock_title), + activity.getString(R.string.education_unlock_summary)) + .dimColor(R.color.green) + .icon(ContextCompat.getDrawable(activity, R.mipmap.ic_launcher_round)) + .textColorInt(Color.WHITE) + .tintTarget(false) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_unlock_key) + } + + fun checkAndPerformedReadOnlyEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationReadOnlyPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_read_only_title), + activity.getString(R.string.education_read_only_summary)) + .textColorInt(Color.WHITE) + .tintTarget(true) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_read_only_key) + } + + fun checkAndPerformedFingerprintEducation(educationView: View, + onEducationViewClick: ((TapTargetView?) -> Unit)? = null, + onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean { + return checkAndPerformedEducation(!isEducationFingerprintPerformed(activity), + TapTarget.forView(educationView, + activity.getString(R.string.education_fingerprint_title), + activity.getString(R.string.education_fingerprint_summary)) + .textColorInt(Color.WHITE) + .tintTarget(false) + .cancelable(true), + object : TapTargetView.Listener() { + override fun onTargetClick(view: TapTargetView) { + super.onTargetClick(view) + onEducationViewClick?.invoke(view) + } + + override fun onOuterCircleClick(view: TapTargetView?) { + super.onOuterCircleClick(view) + view?.dismiss(false) + onOuterViewClick?.invoke(view) + } + }, + R.string.education_fingerprint_key) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java index 1ee943508..08007f6ef 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java @@ -24,15 +24,14 @@ import android.app.Activity; import android.app.assist.AssistStructure; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; -import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -45,23 +44,22 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; -import com.getkeepsafe.taptargetview.TapTarget; -import com.getkeepsafe.taptargetview.TapTargetView; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.activities.GroupActivity; import com.kunzisoft.keepass.autofill.AutofillHelper; import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable; -import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; -import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment; +import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation; +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; import com.kunzisoft.keepass.password.PasswordActivity; import com.kunzisoft.keepass.settings.PreferencesUtil; import com.kunzisoft.keepass.stylish.StylishActivity; +import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.UriUtil; @@ -233,8 +231,40 @@ public class FileDatabaseSelectActivity extends StylishActivity implements } } - // For the first time show education - checkAndPerformedEducation(); + new Handler().post(() -> performedNextEducation(new FileDatabaseSelectActivityEducation(this))); + } + + private void performedNextEducation(FileDatabaseSelectActivityEducation fileDatabaseSelectActivityEducation) { + // If no recent files + if (!fileDatabaseHistory.hasRecentFiles() + && fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation( + createButtonView, + tapTargetView -> { + FileDatabaseSelectActivityPermissionsDispatcher + .openCreateFileDialogFragmentWithPermissionCheck(FileDatabaseSelectActivity.this); + return null; + }, + tapTargetView -> { + // But if the user cancel, it can also select a database + performedNextEducation(fileDatabaseSelectActivityEducation); + return null; + }) + ); + else if (fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation( + browseButtonView, + tapTargetView -> { + keyFileHelper.getOpenFileOnClickViewListener().onClick(tapTargetView); + return null; + }, + tapTargetView -> { + fileDatabaseSelectActivityEducation.checkAndPerformedOpenLinkDatabaseEducation( + fileSelectExpandableButton, + tapTargetView1 -> null, + tapTargetView12 -> null + ); + return null; + } + )); } private void fileNoFoundAction(FileNotFoundException e) { @@ -314,113 +344,6 @@ public class FileDatabaseSelectActivity extends StylishActivity implements mAdapter.notifyDataSetChanged(); } - /** - * Check and display learning views - * Displays the explanation for a database creation then a database selection - */ - private void checkAndPerformedEducation() { - - // If no recent files - if ( !fileDatabaseHistory.hasRecentFiles() ) { - // Try to open the creation base education - if (!PreferencesUtil.isEducationCreateDatabasePerformed(this) ) { - - TapTargetView.showFor(this, - TapTarget.forView(createButtonView, - getString(R.string.education_create_database_title), - getString(R.string.education_create_database_summary)) - .icon(ContextCompat.getDrawable(this, R.drawable.ic_database_plus_white_24dp)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - FileDatabaseSelectActivityPermissionsDispatcher - .openCreateFileDialogFragmentWithPermissionCheck(FileDatabaseSelectActivity.this); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - // But if the user cancel, it can also select a database - checkAndPerformedEducationForSelection(); - } - }); - PreferencesUtil.saveEducationPreference(FileDatabaseSelectActivity.this, - R.string.education_create_db_key); - } - } - else - checkAndPerformedEducationForSelection(); - } - - /** - * Check and display learning views - * Displays the explanation for a database selection - */ - private void checkAndPerformedEducationForSelection() { - if (PreferencesUtil.isEducationScreensEnabled(this)) { - - if (!PreferencesUtil.isEducationSelectDatabasePerformed(this) - && browseButtonView != null) { - - TapTargetView.showFor(FileDatabaseSelectActivity.this, - TapTarget.forView(browseButtonView, - getString(R.string.education_select_database_title), - getString(R.string.education_select_database_summary)) - .icon(ContextCompat.getDrawable(this, R.drawable.ic_folder_white_24dp)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - keyFileHelper.getOpenFileOnClickViewListener().onClick(view); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - - if (!PreferencesUtil.isEducationOpenLinkDatabasePerformed(FileDatabaseSelectActivity.this)) { - - TapTargetView.showFor(FileDatabaseSelectActivity.this, - TapTarget.forView(fileSelectExpandableButton, - getString(R.string.education_open_link_database_title), - getString(R.string.education_open_link_database_summary)) - .icon(ContextCompat.getDrawable(FileDatabaseSelectActivity.this, R.drawable.ic_link_white_24dp)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - // Do nothing here - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - PreferencesUtil.saveEducationPreference(FileDatabaseSelectActivity.this, - R.string.education_open_link_db_key); - } - } - }); - PreferencesUtil.saveEducationPreference(FileDatabaseSelectActivity.this, - R.string.education_select_db_key); - } - } - } - @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 4bcc6c778..685898e4f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -25,7 +25,6 @@ import android.app.assist.AssistStructure; import android.app.backup.BackupManager; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Color; import android.hardware.fingerprint.FingerprintManager; import android.net.Uri; import android.os.Build; @@ -34,7 +33,6 @@ import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; -import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.text.Editable; @@ -44,9 +42,13 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.*; -import com.getkeepsafe.taptargetview.TapTarget; -import com.getkeepsafe.taptargetview.TapTargetView; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.activities.GroupActivity; @@ -59,6 +61,7 @@ import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable; import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; +import com.kunzisoft.keepass.education.PasswordActivityEducation; import com.kunzisoft.keepass.fileselect.KeyFileHelper; import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog; @@ -70,13 +73,20 @@ import com.kunzisoft.keepass.tasks.ActionRunnable; import com.kunzisoft.keepass.utils.EmptyUtils; import com.kunzisoft.keepass.utils.MenuUtil; import com.kunzisoft.keepass.utils.UriUtil; + import org.jetbrains.annotations.Nullable; -import permissions.dispatcher.*; import java.io.File; import java.io.FileNotFoundException; import java.lang.ref.WeakReference; +import permissions.dispatcher.NeedsPermission; +import permissions.dispatcher.OnNeverAskAgain; +import permissions.dispatcher.OnPermissionDenied; +import permissions.dispatcher.OnShowRationale; +import permissions.dispatcher.PermissionRequest; +import permissions.dispatcher.RuntimePermissions; + @RuntimePermissions public class PasswordActivity extends StylishActivity implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback { @@ -325,113 +335,6 @@ public class PasswordActivity extends StylishActivity super.onSaveInstanceState(outState); } - /** - * Check and display learning views - * Displays the explanation for a database opening with fingerprints if available - */ - private void checkAndPerformedEducation(Menu menu) { - if (PreferencesUtil.isEducationScreensEnabled(this)) { - - if (!PreferencesUtil.isEducationUnlockPerformed(this)) { - - TapTargetView.showFor(this, - TapTarget.forView(findViewById(R.id.password_input_container), - getString(R.string.education_unlock_title), - getString(R.string.education_unlock_summary)) - .dimColor(R.color.green) - .icon(ContextCompat.getDrawable(getApplicationContext(), R.mipmap.ic_launcher_round)) - .textColorInt(Color.WHITE) - .tintTarget(false) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - performedReadOnlyEducation(menu); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - performedReadOnlyEducation(menu); - - } - }); - // TODO make a period for donation - PreferencesUtil.saveEducationPreference(PasswordActivity.this, - R.string.education_unlock_key); - } - } - } - - /** - * Check and display learning views - * Displays read-only if available - */ - private void performedReadOnlyEducation(Menu menu) { - if (!PreferencesUtil.isEducationReadOnlyPerformed(this)) { - try { - TapTargetView.showFor(this, - TapTarget.forToolbarMenuItem(toolbar, R.id.menu_open_file_read_mode_key, - getString(R.string.education_read_only_title), - getString(R.string.education_read_only_summary)) - .textColorInt(Color.WHITE) - .tintTarget(true) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onTargetClick(TapTargetView view) { - super.onTargetClick(view); - MenuItem editItem = menu.findItem(R.id.menu_open_file_read_mode_key); - onOptionsItemSelected(editItem); - checkAndPerformedEducationForFingerprint(); - } - - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - checkAndPerformedEducationForFingerprint(); - } - }); - PreferencesUtil.saveEducationPreference(this, - R.string.education_read_only_key); - } catch (Exception e) { - // If icon not visible - Log.w(TAG, "Can't performed education for entry's edition"); - } - } - } - - /** - * Check and display learning views - * Displays fingerprints if available - */ - private void checkAndPerformedEducationForFingerprint() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - - if ( PreferencesUtil.isFingerprintEnable(getApplicationContext()) - && FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager.class))) { - - TapTargetView.showFor(this, - TapTarget.forView(fingerprintImageView, - getString(R.string.education_fingerprint_title), - getString(R.string.education_fingerprint_summary)) - .textColorInt(Color.WHITE) - .tintTarget(false) - .cancelable(true), - new TapTargetView.Listener() { - @Override - public void onOuterCircleClick(TapTargetView view) { - super.onOuterCircleClick(view); - view.dismiss(false); - } - }); - } - } - } - @Override public void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId) { mDbUri = dbUri; @@ -957,11 +860,46 @@ public class PasswordActivity extends StylishActivity super.onCreateOptionsMenu(menu); // Show education views - new Handler().post(() -> checkAndPerformedEducation(menu)); + new Handler().post(() -> performedNextEducation(new PasswordActivityEducation(this), menu)); return true; } + private void performedNextEducation(PasswordActivityEducation passwordActivityEducation, + Menu menu) { + if (passwordActivityEducation.checkAndPerformedFingerprintUnlockEducation( + toolbar, + tapTargetView -> { + performedNextEducation(passwordActivityEducation, menu); + return null; + }, + tapTargetView -> { + performedNextEducation(passwordActivityEducation, menu); + return null; + }) + ); + else if (toolbar.findViewById(R.id.menu_open_file_read_mode_key) != null + && passwordActivityEducation.checkAndPerformedReadOnlyEducation( + toolbar.findViewById(R.id.menu_open_file_read_mode_key), + tapTargetView -> { + onOptionsItemSelected(menu.findItem(R.id.menu_open_file_read_mode_key)); + performedNextEducation(passwordActivityEducation, menu); + return null; + }, + tapTargetView -> { + performedNextEducation(passwordActivityEducation, menu); + return null; + }) + ); + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + && PreferencesUtil.isFingerprintEnable(getApplicationContext()) + && FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager.class)) + && passwordActivityEducation.checkAndPerformedFingerprintEducation(fingerprintImageView, + tapTargetView -> null, + tapTargetView -> null) + ); + } + private void changeOpenFileReadIcon(MenuItem togglePassword) { if ( readOnly ) { togglePassword.setTitle(R.string.menu_file_selection_read_only); diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index e9e4cea73..768370de8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -49,6 +49,7 @@ import com.kunzisoft.keepass.database.element.Database; import com.kunzisoft.keepass.dialogs.ProFeatureDialogFragment; import com.kunzisoft.keepass.dialogs.UnavailableFeatureDialogFragment; import com.kunzisoft.keepass.dialogs.UnderDevelopmentFeatureDialogFragment; +import com.kunzisoft.keepass.education.Education; import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; import com.kunzisoft.keepass.icons.IconPackChooser; @@ -392,7 +393,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat Preference stylePreference = findPreference(getString(R.string.setting_style_key)); stylePreference.setOnPreferenceChangeListener((preference, newValue) -> { String styleIdString = (String) newValue; - if (!(!BuildConfig.CLOSED_STORE && PreferencesUtil.isEducationScreenReclickedPerformed(getContext()))) + if (!(!BuildConfig.CLOSED_STORE && Education.Companion.isEducationScreenReclickedPerformed(getContext()))) for (String themeIdDisabled : BuildConfig.STYLES_DISABLED) { if (themeIdDisabled.equals(styleIdString)) { ProFeatureDialogFragment dialogFragment = new ProFeatureDialogFragment(); @@ -411,7 +412,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat Preference iconPackPreference = findPreference(getString(R.string.setting_icon_pack_choose_key)); iconPackPreference.setOnPreferenceChangeListener((preference, newValue) -> { String iconPackId = (String) newValue; - if (!(!BuildConfig.CLOSED_STORE && PreferencesUtil.isEducationScreenReclickedPerformed(getContext()))) + if (!(!BuildConfig.CLOSED_STORE && Education.Companion.isEducationScreenReclickedPerformed(getContext()))) for (String iconPackIdDisabled : BuildConfig.ICON_PACKS_DISABLED) { if (iconPackIdDisabled.equals(iconPackId)) { ProFeatureDialogFragment dialogFragment = new ProFeatureDialogFragment(); @@ -429,9 +430,9 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat resetEducationScreens.setOnPreferenceClickListener(preference -> { // To allow only one toast if (count == 0) { - SharedPreferences sharedPreferences = PreferencesUtil.getEducationSharedPreferences(getContext()); + SharedPreferences sharedPreferences = Education.Companion.getEducationSharedPreferences(getContext()); SharedPreferences.Editor editor = sharedPreferences.edit(); - for (int resourceId : PreferencesUtil.educationResourceKeys) { + for (int resourceId : Education.Companion.getEducationResourcesKeys()) { editor.putBoolean(getString(resourceId), false); } editor.apply(); @@ -487,7 +488,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat super.onStop(); if(count==10) { if (getActivity()!=null) - PreferencesUtil.getEducationSharedPreferences(getActivity()).edit() + Education.Companion.getEducationSharedPreferences(getActivity()).edit() .putBoolean(getString(R.string.education_screen_reclicked_key), true).apply(); } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java index f1426f7d3..590ec481a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.java @@ -33,7 +33,6 @@ import java.util.Set; public class PreferencesUtil { private static final String NO_BACKUP_PREFERENCE_FILE_NAME = "nobackup"; - private static final String EDUCATION_PREFERENCE = "kdbxeducation"; public static SharedPreferences getNoBackupSharedPreferences(Context ctx) { return ctx.getSharedPreferences( @@ -41,12 +40,6 @@ public class PreferencesUtil { Context.MODE_PRIVATE); } - public static SharedPreferences getEducationSharedPreferences(Context ctx) { - return ctx.getSharedPreferences( - PreferencesUtil.EDUCATION_PREFERENCE, - Context.MODE_PRIVATE); - } - public static void deleteAllValuesFromNoBackupPreferences(Context ctx) { SharedPreferences prefsNoBackup = getNoBackupSharedPreferences(ctx); SharedPreferences.Editor sharedPreferencesEditor = prefsNoBackup.edit(); @@ -220,212 +213,4 @@ public class PreferencesUtil { return prefs.getBoolean(context.getString(R.string.keyboard_key_sound_key), context.getResources().getBoolean(R.bool.keyboard_key_sound_default)); } - - /** - * All preference keys associated with education - */ - public static int[] educationResourceKeys = new int[] { - R.string.education_create_db_key, - R.string.education_select_db_key, - R.string.education_open_link_db_key, - R.string.education_unlock_key, - R.string.education_read_only_key, - R.string.education_search_key, - R.string.education_new_node_key, - R.string.education_sort_key, - R.string.education_lock_key, - R.string.education_copy_username_key, - R.string.education_entry_edit_key, - R.string.education_password_generator_key, - R.string.education_entry_new_field_key - }; - - public static boolean isEducationScreensEnabled(Context context) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - return sharedPreferences.getBoolean(context.getString(R.string.enable_education_screens_key), - context.getResources().getBoolean(R.bool.enable_education_screens_default)); - } - - /** - * Register education preferences as true in EDUCATION_PREFERENCE SharedPreferences - * - * @param context The context to retrieve the key string in XML - * @param educationKeys Keys to save as boolean 'true' - */ - public static void saveEducationPreference(Context context, int... educationKeys) { - SharedPreferences sharedPreferences = PreferencesUtil.getEducationSharedPreferences(context); - SharedPreferences.Editor editor = sharedPreferences.edit(); - for (int key : educationKeys) { - editor.putBoolean(context.getString(key), true); - } - editor.apply(); - } - - /** - * Determines whether the explanatory view of the database creation has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_create_db_key key - */ - public static boolean isEducationCreateDatabasePerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_create_db_key), - context.getResources().getBoolean(R.bool.education_create_db_default)); - } - - /** - * Determines whether the explanatory view of the database selection has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_select_db_key key - */ - public static boolean isEducationSelectDatabasePerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_select_db_key), - context.getResources().getBoolean(R.bool.education_select_db_default)); - } - - /** - * Determines whether the explanatory view of the database selection has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_select_db_key key - */ - public static boolean isEducationOpenLinkDatabasePerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_open_link_db_key), - context.getResources().getBoolean(R.bool.education_open_link_db_default)); - } - - /** - * Determines whether the explanatory view of the database unlock has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_unlock_key key - */ - public static boolean isEducationUnlockPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_unlock_key), - context.getResources().getBoolean(R.bool.education_unlock_default)); - } - - /** - * Determines whether the explanatory view of the database read-only has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_read_only_key key - */ - public static boolean isEducationReadOnlyPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_read_only_key), - context.getResources().getBoolean(R.bool.education_read_only_default)); - } - - /** - * Determines whether the explanatory view of search has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_search_key key - */ - public static boolean isEducationSearchPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_search_key), - context.getResources().getBoolean(R.bool.education_search_default)); - } - - /** - * Determines whether the explanatory view of add new node has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_new_node_key key - */ - public static boolean isEducationNewNodePerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_new_node_key), - context.getResources().getBoolean(R.bool.education_new_node_default)); - } - - /** - * Determines whether the explanatory view of the sort has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_sort_key key - */ - public static boolean isEducationSortPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_sort_key), - context.getResources().getBoolean(R.bool.education_sort_default)); - } - - /** - * Determines whether the explanatory view of the database lock has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_lock_key key - */ - public static boolean isEducationLockPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_lock_key), - context.getResources().getBoolean(R.bool.education_lock_default)); - } - - /** - * Determines whether the explanatory view of the username copy has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_copy_username_key key - */ - public static boolean isEducationCopyUsernamePerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_copy_username_key), - context.getResources().getBoolean(R.bool.education_copy_username_key)); - } - - /** - * Determines whether the explanatory view of the entry edition has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_entry_edit_key key - */ - public static boolean isEducationEntryEditPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_entry_edit_key), - context.getResources().getBoolean(R.bool.education_entry_edit_default)); - } - - /** - * Determines whether the explanatory view of the password generator has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_password_generator_key key - */ - public static boolean isEducationPasswordGeneratorPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_password_generator_key), - context.getResources().getBoolean(R.bool.education_password_generator_default)); - } - - /** - * Determines whether the explanatory view of the new fields button in an entry has already been displayed. - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_entry_new_field_key key - */ - public static boolean isEducationEntryNewFieldPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_entry_new_field_key), - context.getResources().getBoolean(R.bool.education_entry_new_field_default)); - } - - /** - * Defines if the reset education preference has been reclicked - * - * @param context The context to open the SharedPreferences - * @return boolean value of education_screen_reclicked_key key - */ - public static boolean isEducationScreenReclickedPerformed(Context context) { - SharedPreferences prefs = getEducationSharedPreferences(context); - return prefs.getBoolean(context.getString(R.string.education_screen_reclicked_key), - context.getResources().getBoolean(R.bool.education_screen_reclicked_default)); - } } diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 4155e7112..c39912a4a 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -181,6 +181,8 @@ false education_read_only_key false + education_fingerprint_key + false education_search_key false education_new_node_key From 7dfe85450d1ff5ac361ef88bac4f51c1daffbb9b Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 16:37:57 +0200 Subject: [PATCH 133/289] Kotlinized EntryActivity --- .../keepass/activities/EntryActivity.java | 484 ------------------ .../keepass/activities/EntryActivity.kt | 389 ++++++++++++++ .../keepass/activities/GroupActivity.java | 2 +- .../NotificationEntryCopyManager.kt | 90 ++++ .../keepass/view/EntryContentsView.java | 253 --------- .../keepass/view/EntryContentsView.kt | 250 +++++++++ 6 files changed, 730 insertions(+), 738 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java deleted file mode 100644 index 09a27ea44..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.activities; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.lock.LockingHideActivity; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.EntryVersioned; -import com.kunzisoft.keepass.database.element.PwNodeId; -import com.kunzisoft.keepass.database.element.security.ProtectedString; -import com.kunzisoft.keepass.education.EntryActivityEducation; -import com.kunzisoft.keepass.notifications.NotificationCopyingService; -import com.kunzisoft.keepass.notifications.NotificationField; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.settings.SettingsAutofillActivity; -import com.kunzisoft.keepass.timeout.ClipboardHelper; -import com.kunzisoft.keepass.timeout.TimeoutHelper; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.utils.Util; -import com.kunzisoft.keepass.view.EntryContentsView; - -import java.util.ArrayList; -import java.util.Date; - -import kotlin.Unit; -import kotlin.jvm.functions.Function2; - -import static com.kunzisoft.keepass.settings.PreferencesUtil.isClipboardNotificationsEnable; -import static com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields; - -public class EntryActivity extends LockingHideActivity { - private final static String TAG = EntryActivity.class.getName(); - - public static final String KEY_ENTRY = "entry"; - - private ImageView titleIconView; - private TextView titleView; - private EntryContentsView entryContentsView; - private Toolbar toolbar; - - protected EntryVersioned mEntry; - private boolean mShowPassword; - - private ClipboardHelper clipboardHelper; - private boolean firstLaunchOfActivity; - - private int iconColor; - - public static void launch(Activity activity, EntryVersioned pw, boolean readOnly) { - if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { - Intent intent = new Intent(activity, EntryActivity.class); - intent.putExtra(KEY_ENTRY, pw.getNodeId()); - ReadOnlyHelper.INSTANCE.putReadOnlyInIntent(intent, readOnly); - activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.entry_view); - - toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - assert getSupportActionBar() != null; - getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - - Database db = App.Companion.getCurrentDatabase(); - setReadOnly(db.isReadOnly() || getReadOnly()); - - mShowPassword = !PreferencesUtil.isPasswordMask(this); - - // Get Entry from UUID - Intent i = getIntent(); - PwNodeId keyEntry; - try { - keyEntry = i.getParcelableExtra(KEY_ENTRY); - mEntry = db.getEntryById(keyEntry); - } catch (ClassCastException e) { - Log.e(TAG, "Unable to retrieve the entry key"); - } - if (mEntry == null) { - Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show(); - finish(); - return; - } - - // Update last access time. - mEntry.touch(false, false); - - // Retrieve the textColor to tint the icon - int[] attrs = {R.attr.textColorInverse}; - TypedArray ta = getTheme().obtainStyledAttributes(attrs); - iconColor = ta.getColor(0, Color.WHITE); - - // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set - invalidateOptionsMenu(); - - // Get views - titleIconView = findViewById(R.id.entry_icon); - titleView = findViewById(R.id.entry_title); - entryContentsView = findViewById(R.id.entry_contents); - entryContentsView.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this)); - - // Init the clipboard helper - clipboardHelper = new ClipboardHelper(this); - firstLaunchOfActivity = true; - } - - @Override - protected void onResume() { - super.onResume(); - - // Fill data in resume to update from EntryEditActivity - fillData(); - invalidateOptionsMenu(); - - Database database = App.Companion.getCurrentDatabase(); - // Start to manage field reference to copy a value from ref - database.startManageEntry(mEntry); - - boolean containsUsernameToCopy = - mEntry.getUsername().length() > 0; - boolean containsPasswordToCopy = - (mEntry.getPassword().length() > 0 - && PreferencesUtil.allowCopyPasswordAndProtectedFields(this)); - boolean containsExtraFieldToCopy = - (mEntry.allowExtraFields() - && ((mEntry.containsCustomFields() - && mEntry.containsCustomFieldsNotProtected()) - || (mEntry.containsCustomFields() - && mEntry.containsCustomFieldsProtected() - && PreferencesUtil.allowCopyPasswordAndProtectedFields(this)) - ) - ); - - // If notifications enabled in settings - // Don't if application timeout - if (firstLaunchOfActivity && isClipboardNotificationsEnable(getApplicationContext())) { - if (containsUsernameToCopy - || containsPasswordToCopy - || containsExtraFieldToCopy - ) { - // username already copied, waiting for user's action before copy password. - Intent intent = new Intent(this, NotificationCopyingService.class); - intent.setAction(NotificationCopyingService.ACTION_NEW_NOTIFICATION); - if (mEntry.getTitle() != null) - intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getTitle()); - // Construct notification fields - ArrayList notificationFields = new ArrayList<>(); - // Add username if exists to notifications - if (containsUsernameToCopy) - notificationFields.add( - new NotificationField( - NotificationField.NotificationFieldId.USERNAME, - mEntry.getUsername(), - getResources())); - // Add password to notifications - if (containsPasswordToCopy) { - notificationFields.add( - new NotificationField( - NotificationField.NotificationFieldId.PASSWORD, - mEntry.getPassword(), - getResources())); - } - // Add extra fields - if (containsExtraFieldToCopy) { - try { - mEntry.getFields().doActionToAllCustomProtectedField(new Function2() { - private int anonymousFieldNumber = 0; - @Override - public Unit invoke(String key, ProtectedString value) { - //If value is not protected or allowed - if (!value.isProtected() || PreferencesUtil.allowCopyPasswordAndProtectedFields(EntryActivity.this)) { - notificationFields.add( - new NotificationField( - NotificationField.NotificationFieldId.getAnonymousFieldId()[anonymousFieldNumber], - value.toString(), - key, - getResources())); - anonymousFieldNumber++; - } - return null; - } - }); - } catch (ArrayIndexOutOfBoundsException e) { - Log.w(TAG, "Only " + NotificationField.NotificationFieldId.getAnonymousFieldId().length + - " anonymous notifications are available"); - } - } - // Add notifications - intent.putParcelableArrayListExtra(NotificationCopyingService.EXTRA_FIELDS, notificationFields); - - startService(intent); - } - - database.stopManageEntry(mEntry); - } - firstLaunchOfActivity = false; - } - - protected void fillData() { - Database database = App.Companion.getCurrentDatabase(); - database.startManageEntry(mEntry); - // Assign title icon - database.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); - - // Assign title text - titleView.setText(mEntry.getVisualTitle()); - - // Assign basic fields - entryContentsView.assignUserName(mEntry.getUsername()); - entryContentsView.assignUserNameCopyListener(view -> - clipboardHelper.timeoutCopyToClipboard(mEntry.getUsername(), - getString(R.string.copy_field, getString(R.string.entry_user_name))) - ); - - boolean allowCopyPassword = PreferencesUtil.allowCopyPasswordAndProtectedFields(this); - entryContentsView.assignPassword(mEntry.getPassword(), allowCopyPassword); - if (allowCopyPassword) { - entryContentsView.assignPasswordCopyListener(view -> - clipboardHelper.timeoutCopyToClipboard(mEntry.getPassword(), - getString(R.string.copy_field, getString(R.string.entry_password))) - ); - } else { - // If dialog not already shown - if (isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)) { - entryContentsView.assignPasswordCopyListener(v -> { - String message = getString(R.string.allow_copy_password_warning) + - "\n\n" + - getString(R.string.clipboard_warning); - AlertDialog warningDialog = new AlertDialog.Builder(EntryActivity.this) - .setMessage(message).create(); - warningDialog.setButton(AlertDialog.BUTTON1, getText(android.R.string.ok), - (dialog, which) -> { - PreferencesUtil.setAllowCopyPasswordAndProtectedFields(EntryActivity.this, true); - dialog.dismiss(); - fillData(); - }); - warningDialog.setButton(AlertDialog.BUTTON2, getText(android.R.string.cancel), - (dialog, which) -> { - PreferencesUtil.setAllowCopyPasswordAndProtectedFields(EntryActivity.this, false); - dialog.dismiss(); - fillData(); - }); - warningDialog.show(); - }); - } else { - entryContentsView.assignPasswordCopyListener(null); - } - } - - entryContentsView.assignURL(mEntry.getUrl()); - - entryContentsView.setHiddenPasswordStyle(!mShowPassword); - entryContentsView.assignComment(mEntry.getNotes()); - - // Assign custom fields - if (mEntry.allowExtraFields()) { - entryContentsView.clearExtraFields(); - - mEntry.getFields().doActionToAllCustomProtectedField((label, value) -> { - boolean showAction = (!value.isProtected() || PreferencesUtil.allowCopyPasswordAndProtectedFields(EntryActivity.this)); - entryContentsView.addExtraField(label, value, showAction, view -> - clipboardHelper.timeoutCopyToClipboard( - value.toString(), - getString(R.string.copy_field, label) - ) - ); - return null; - }); - } - - // Assign dates - entryContentsView.assignCreationDate(mEntry.getCreationTime().getDate()); - entryContentsView.assignModificationDate(mEntry.getLastModificationTime().getDate()); - entryContentsView.assignLastAccessDate(mEntry.getLastAccessTime().getDate()); - Date expires = mEntry.getExpiryTime().getDate(); - if ( mEntry.isExpires() ) { - entryContentsView.assignExpiresDate(expires); - } else { - entryContentsView.assignExpiresDate(getString(R.string.never)); - } - - database.stopManageEntry(mEntry); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE: - // Not directly get the entry from intent data but from database - fillData(); - break; - } - } - - private void changeShowPasswordIcon(MenuItem togglePassword) { - if ( mShowPassword ) { - togglePassword.setTitle(R.string.menu_hide_password); - togglePassword.setIcon(R.drawable.ic_visibility_off_white_24dp); - } else { - togglePassword.setTitle(R.string.menu_showpass); - togglePassword.setIcon(R.drawable.ic_visibility_white_24dp); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - - MenuInflater inflater = getMenuInflater(); - MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); - inflater.inflate(R.menu.entry, menu); - inflater.inflate(R.menu.database_lock, menu); - - if (getReadOnly()) { - MenuItem edit = menu.findItem(R.id.menu_edit); - if (edit != null) - edit.setVisible(false); - } - - MenuItem togglePassword = menu.findItem(R.id.menu_toggle_pass); - if (entryContentsView != null && togglePassword != null) { - if (entryContentsView.isPasswordPresent() || entryContentsView.atLeastOneFieldProtectedPresent()) { - changeShowPasswordIcon(togglePassword); - } else { - togglePassword.setVisible(false); - } - } - - MenuItem gotoUrl = menu.findItem(R.id.menu_goto_url); - if (gotoUrl != null) { - // In API >= 11 onCreateOptionsMenu may be called before onCreate completes - // so mEntry may not be set - if (mEntry == null) { - gotoUrl.setVisible(false); - } else { - String url = mEntry.getUrl(); - if (EmptyUtils.INSTANCE.isNullOrEmpty(url)) { - // disable button if url is not available - gotoUrl.setVisible(false); - } - } - } - - // Show education views - new Handler().post(() -> performedNextEducation(new EntryActivityEducation(this), menu)); - - return true; - } - - private void performedNextEducation(EntryActivityEducation entryActivityEducation, - Menu menu) { - if (entryContentsView != null - && entryContentsView.isUserNamePresent() - && entryActivityEducation.checkAndPerformedEntryCopyEducation( - findViewById(R.id.entry_user_name_action_image), - tapTargetView -> { - clipboardHelper.timeoutCopyToClipboard(mEntry.getUsername(), - getString(R.string.copy_field, - getString(R.string.entry_user_name))); - return null; - }, - tapTargetView -> { - // Launch autofill settings - startActivity(new Intent(EntryActivity.this, SettingsAutofillActivity.class)); - return null; - }) - ); - else if (toolbar.findViewById(R.id.menu_edit) != null - && entryActivityEducation.checkAndPerformedEntryEditEducation( - toolbar.findViewById(R.id.menu_edit), - tapTargetView -> { - onOptionsItemSelected(menu.findItem(R.id.menu_edit)); - return null; - }, - tapTargetView -> { - // Open Keepass doc to create field references - startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse(getString(R.string.field_references_url)))); - return null; - }) - ); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch ( item.getItemId() ) { - case R.id.menu_contribute: - return MenuUtil.INSTANCE.onContributionItemSelected(this); - - case R.id.menu_toggle_pass: - mShowPassword = !mShowPassword; - changeShowPasswordIcon(item); - entryContentsView.setHiddenPasswordStyle(!mShowPassword); - return true; - - case R.id.menu_edit: - EntryEditActivity.launch(EntryActivity.this, mEntry); - return true; - - case R.id.menu_goto_url: - String url; - url = mEntry.getUrl(); - - // Default http:// if no protocol specified - if ( ! url.contains("://") ) { - url = "http://" + url; - } - - try { - Util.gotoUrl(this, url); - } catch (ActivityNotFoundException e) { - Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show(); - } - return true; - - case R.id.menu_lock: - lockAndExit(); - return true; - - case android.R.id.home : - finish(); // close this activity and return to preview activity (if there is any) - } - - return super.onOptionsItemSelected(item); - } - - - @Override - public void finish() { - // Transit data in previous Activity after an update - /* - TODO Slowdown when add entry as result - Intent intent = new Intent(); - intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry); - setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent); - */ - super.finish(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt new file mode 100644 index 000000000..afe2d1d2a --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -0,0 +1,389 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + */ +package com.kunzisoft.keepass.activities + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.graphics.Color +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.support.v7.app.AlertDialog +import android.support.v7.widget.Toolbar +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import android.widget.Toast +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.lock.LockingHideActivity +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.PwNodeId +import com.kunzisoft.keepass.education.EntryActivityEducation +import com.kunzisoft.keepass.notifications.NotificationEntryCopyManager +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields +import com.kunzisoft.keepass.settings.SettingsAutofillActivity +import com.kunzisoft.keepass.timeout.ClipboardHelper +import com.kunzisoft.keepass.timeout.TimeoutHelper +import com.kunzisoft.keepass.utils.MenuUtil +import com.kunzisoft.keepass.utils.Util +import com.kunzisoft.keepass.view.EntryContentsView + +class EntryActivity : LockingHideActivity() { + + private var titleIconView: ImageView? = null + private var titleView: TextView? = null + private var entryContentsView: EntryContentsView? = null + private var toolbar: Toolbar? = null + + private var mEntry: EntryVersioned? = null + private var mShowPassword: Boolean = false + + private var clipboardHelper: ClipboardHelper? = null + private var firstLaunchOfActivity: Boolean = false + + private var iconColor: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.entry_view) + + toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + + val currentDatabase = App.currentDatabase + readOnly = currentDatabase.isReadOnly || readOnly + + mShowPassword = !PreferencesUtil.isPasswordMask(this) + + // Get Entry from UUID + try { + val keyEntry: PwNodeId<*> = intent.getParcelableExtra(KEY_ENTRY) + mEntry = currentDatabase.getEntryById(keyEntry) + } catch (e: ClassCastException) { + Log.e(TAG, "Unable to retrieve the entry key") + } + + if (mEntry == null) { + Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show() + finish() + return + } + + // Update last access time. + mEntry?.touch(modified = false, touchParents = false) + + // Retrieve the textColor to tint the icon + iconColor = theme. + obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) + .getColor(0, Color.WHITE) + + // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set + invalidateOptionsMenu() + + // Get views + titleIconView = findViewById(R.id.entry_icon) + titleView = findViewById(R.id.entry_title) + entryContentsView = findViewById(R.id.entry_contents) + entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this)) + + // Init the clipboard helper + clipboardHelper = ClipboardHelper(this) + firstLaunchOfActivity = true + } + + override fun onResume() { + super.onResume() + + mEntry?.let { entry -> + // Fill data in resume to update from EntryEditActivity + fillEntryDataInContentsView(entry) + // Refresh Menu + invalidateOptionsMenu() + // Manage entry copy to start notification if allowed + NotificationEntryCopyManager.launchNotificationIfAllowed(this, + firstLaunchOfActivity, + entry) + } + + firstLaunchOfActivity = false + } + + private fun fillEntryDataInContentsView(entry: EntryVersioned) { + + val database = App.currentDatabase + database.startManageEntry(entry) + // Assign title icon + database.drawFactory.assignDatabaseIconTo(this, titleIconView, entry.icon, iconColor) + + // Assign title text + titleView?.text = entry.getVisualTitle() + + // Assign basic fields + entryContentsView?.assignUserName(entry.username) + entryContentsView?.assignUserNameCopyListener(View.OnClickListener { + clipboardHelper?.timeoutCopyToClipboard(entry.username, + getString(R.string.copy_field, + getString(R.string.entry_user_name))) + }) + + val allowCopyPassword = PreferencesUtil.allowCopyPasswordAndProtectedFields(this) + entryContentsView?.assignPassword(entry.password, allowCopyPassword) + if (allowCopyPassword) { + entryContentsView?.assignPasswordCopyListener(View.OnClickListener { + clipboardHelper?.timeoutCopyToClipboard(entry.password, + getString(R.string.copy_field, + getString(R.string.entry_password))) + }) + } else { + // If dialog not already shown + if (isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)) { + entryContentsView?.assignPasswordCopyListener(View.OnClickListener { + val message = getString(R.string.allow_copy_password_warning) + + "\n\n" + + getString(R.string.clipboard_warning) + val warningDialog = AlertDialog.Builder(this@EntryActivity) + .setMessage(message).create() + warningDialog.setButton(AlertDialog.BUTTON1, getText(android.R.string.ok) + ) { dialog, _ -> + PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true) + dialog.dismiss() + fillEntryDataInContentsView(entry) + } + warningDialog.setButton(AlertDialog.BUTTON2, getText(android.R.string.cancel) + ) { dialog, _ -> + PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false) + dialog.dismiss() + fillEntryDataInContentsView(entry) + } + warningDialog.show() + }) + } else { + entryContentsView?.assignPasswordCopyListener(null) + } + } + + entryContentsView?.assignURL(entry.url) + entryContentsView?.setHiddenPasswordStyle(!mShowPassword) + entryContentsView?.assignComment(entry.notes) + + // Assign custom fields + if (entry.allowExtraFields()) { + entryContentsView?.clearExtraFields() + + entry.fields.doActionToAllCustomProtectedField { label, value -> + val showAction = !value.isProtected || PreferencesUtil.allowCopyPasswordAndProtectedFields(this@EntryActivity) + entryContentsView?.addExtraField(label, value, showAction, View.OnClickListener { + clipboardHelper?.timeoutCopyToClipboard( + value.toString(), + getString(R.string.copy_field, label) + ) + }) + } + } + + // Assign dates + entry.creationTime.date?.let { + entryContentsView?.assignCreationDate(it) + } + entry.lastModificationTime.date?.let { + entryContentsView?.assignModificationDate(it) + } + entry.lastAccessTime.date?.let { + entryContentsView?.assignLastAccessDate(it) + } + val expires = entry.expiryTime.date + if (entry.isExpires && expires != null) { + entryContentsView?.assignExpiresDate(expires) + } else { + entryContentsView?.assignExpiresDate(getString(R.string.never)) + } + + database.stopManageEntry(entry) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE -> + // Not directly get the entry from intent data but from database + mEntry?.let { + fillEntryDataInContentsView(it) + } + } + } + + private fun changeShowPasswordIcon(togglePassword: MenuItem?) { + if (mShowPassword) { + togglePassword?.setTitle(R.string.menu_hide_password) + togglePassword?.setIcon(R.drawable.ic_visibility_off_white_24dp) + } else { + togglePassword?.setTitle(R.string.menu_showpass) + togglePassword?.setIcon(R.drawable.ic_visibility_white_24dp) + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + + val inflater = menuInflater + MenuUtil.contributionMenuInflater(inflater, menu) + inflater.inflate(R.menu.entry, menu) + inflater.inflate(R.menu.database_lock, menu) + + if (readOnly) { + menu.findItem(R.id.menu_edit)?.isVisible = false + } + + val togglePassword = menu.findItem(R.id.menu_toggle_pass) + entryContentsView?.let { + if (it.isPasswordPresent || it.atLeastOneFieldProtectedPresent()) { + changeShowPasswordIcon(togglePassword) + } else { + togglePassword?.isVisible = false + } + } + + val gotoUrl = menu.findItem(R.id.menu_goto_url) + gotoUrl?.apply { + // In API >= 11 onCreateOptionsMenu may be called before onCreate completes + // so mEntry may not be set + if (mEntry == null) { + isVisible = false + } else { + if (mEntry?.url?.isEmpty() != false) { + // disable button if url is not available + isVisible = false + } + } + } + + // Show education views + Handler().post { performedNextEducation(EntryActivityEducation(this), menu) } + + return true + } + + private fun performedNextEducation(entryActivityEducation: EntryActivityEducation, + menu: Menu) { + if (entryContentsView?.isUserNamePresent == true + && entryActivityEducation.checkAndPerformedEntryCopyEducation( + findViewById(R.id.entry_user_name_action_image), + { + clipboardHelper?.timeoutCopyToClipboard(mEntry!!.username, + getString(R.string.copy_field, + getString(R.string.entry_user_name))) + }, + { + // Launch autofill settings + startActivity(Intent(this@EntryActivity, SettingsAutofillActivity::class.java)) + })) + else if (toolbar?.findViewById(R.id.menu_edit) != null && entryActivityEducation.checkAndPerformedEntryEditEducation( + toolbar!!.findViewById(R.id.menu_edit), + { + onOptionsItemSelected(menu.findItem(R.id.menu_edit)) + }, + { + // Open Keepass doc to create field references + startActivity(Intent(Intent.ACTION_VIEW, + Uri.parse(getString(R.string.field_references_url)))) + })) + ; + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this) + + R.id.menu_toggle_pass -> { + mShowPassword = !mShowPassword + changeShowPasswordIcon(item) + entryContentsView?.setHiddenPasswordStyle(!mShowPassword) + return true + } + + R.id.menu_edit -> { + EntryEditActivity.launch(this@EntryActivity, mEntry) + return true + } + + R.id.menu_goto_url -> { + var url: String = mEntry?.url ?: "" + + // Default http:// if no protocol specified + if (!url.contains("://")) { + url = "http://$url" + } + + try { + Util.gotoUrl(this, url) + } catch (e: ActivityNotFoundException) { + Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show() + } + + return true + } + + R.id.menu_lock -> { + lockAndExit() + return true + } + + android.R.id.home -> finish() // close this activity and return to preview activity (if there is any) + } + + return super.onOptionsItemSelected(item) + } + + + override fun finish() { + // Transit data in previous Activity after an update + /* + TODO Slowdown when add entry as result + Intent intent = new Intent(); + intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry); + setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent); + */ + super.finish() + } + + companion object { + private val TAG = EntryActivity::class.java.name + + const val KEY_ENTRY = "entry" + + fun launch(activity: Activity, pw: EntryVersioned, readOnly: Boolean) { + if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { + val intent = Intent(activity, EntryActivity::class.java) + intent.putExtra(KEY_ENTRY, pw.nodeId) + ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly) + activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 3b69e8688..4a0266b63 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -503,7 +503,7 @@ public class GroupActivity extends LockingActivity EntryVersioned entry = ((EntryVersioned) node); EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), () -> { - EntryActivity.launch(GroupActivity.this, entry, getReadOnly()); + EntryActivity.Companion.launch(GroupActivity.this, entry, getReadOnly()); return null; }, () -> { diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt new file mode 100644 index 000000000..1573dea8d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt @@ -0,0 +1,90 @@ +package com.kunzisoft.keepass.notifications + +import android.content.Context +import android.content.Intent +import android.util.Log +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.security.ProtectedString +import com.kunzisoft.keepass.settings.PreferencesUtil +import java.util.* + + +object NotificationEntryCopyManager { + + fun launchNotificationIfAllowed(context: Context, firstLaunch: Boolean, entry: EntryVersioned) { + // Start to manage field reference to copy a value from ref + val database = App.currentDatabase + database.startManageEntry(entry) + + val containsUsernameToCopy = entry.username.isNotEmpty() + val containsPasswordToCopy = entry.password.isNotEmpty() + && PreferencesUtil.allowCopyPasswordAndProtectedFields(context) + val containsExtraFieldToCopy = entry.allowExtraFields() + && (entry.containsCustomFields() + && entry.containsCustomFieldsNotProtected() + || (entry.containsCustomFields() + && entry.containsCustomFieldsProtected() + && PreferencesUtil.allowCopyPasswordAndProtectedFields(context)) + ) + + // If notifications enabled in settings + // Don't if application timeout + if (firstLaunch + && PreferencesUtil.isClipboardNotificationsEnable(context.applicationContext)) { + if (containsUsernameToCopy || containsPasswordToCopy || containsExtraFieldToCopy) { + + // username already copied, waiting for user's action before copy password. + val intent = Intent(context, NotificationCopyingService::class.java) + intent.action = NotificationCopyingService.ACTION_NEW_NOTIFICATION + intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, entry.title) + // Construct notification fields + val notificationFields = ArrayList() + // Add username if exists to notifications + if (containsUsernameToCopy) + notificationFields.add( + NotificationField( + NotificationField.NotificationFieldId.USERNAME, + entry.username, + context.resources)) + // Add password to notifications + if (containsPasswordToCopy) { + notificationFields.add( + NotificationField( + NotificationField.NotificationFieldId.PASSWORD, + entry.password, + context.resources)) + } + // Add extra fields + if (containsExtraFieldToCopy) { + try { + entry.fields.doActionToAllCustomProtectedField(object : (String, ProtectedString) -> Unit { + private var anonymousFieldNumber = 0 + override fun invoke(key: String, value: ProtectedString) { + //If value is not protected or allowed + if (!value.isProtected + || PreferencesUtil.allowCopyPasswordAndProtectedFields(context)) { + notificationFields.add( + NotificationField( + NotificationField.NotificationFieldId.getAnonymousFieldId()[anonymousFieldNumber], + value.toString(), + key, + context.resources)) + anonymousFieldNumber++ + } + } + }) + } catch (e: ArrayIndexOutOfBoundsException) { + Log.w("NotificationEntryCopyMg", "Only " + NotificationField.NotificationFieldId.getAnonymousFieldId().size + + " anonymous notifications are available") + } + + } + // Add notifications + intent.putParcelableArrayListExtra(NotificationCopyingService.EXTRA_FIELDS, notificationFields) + context.startService(intent) + } + } + database.stopManageEntry(entry) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java deleted file mode 100644 index 9eeea705d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.view; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.support.v4.content.ContextCompat; -import android.text.method.PasswordTransformationMethod; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.element.security.ProtectedString; -import com.kunzisoft.keepass.utils.Util; - -import java.text.DateFormat; -import java.util.Date; - -public class EntryContentsView extends LinearLayout { - - private boolean fontInVisibility; - private int colorAccent; - - private View userNameContainerView; - private TextView userNameView; - private ImageView userNameActionView; - - private View passwordContainerView; - private TextView passwordView; - private ImageView passwordActionView; - - private View urlContainerView; - private TextView urlView; - - private View commentContainerView; - private TextView commentView; - - private ViewGroup extrasView; - - private DateFormat dateFormat; - private DateFormat timeFormat; - - private TextView creationDateView; - private TextView modificationDateView; - private TextView lastAccessDateView; - private TextView expiresDateView; - - public EntryContentsView(Context context) { - this(context, null); - } - - public EntryContentsView(Context context, AttributeSet attrs) { - super(context, attrs); - - fontInVisibility = false; - - dateFormat = android.text.format.DateFormat.getDateFormat(context); - timeFormat = android.text.format.DateFormat.getTimeFormat(context); - - inflate(context); - - int[] attrColorAccent = {R.attr.colorAccentCompat}; - TypedArray taColorAccent = context.getTheme().obtainStyledAttributes(attrColorAccent); - this.colorAccent = taColorAccent.getColor(0, Color.BLACK); - taColorAccent.recycle(); - } - - private void inflate(Context context) { - LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - assert inflater != null; - inflater.inflate(R.layout.entry_view_contents, this); - - userNameContainerView = findViewById(R.id.entry_user_name_container); - userNameView = findViewById(R.id.entry_user_name); - userNameActionView = findViewById(R.id.entry_user_name_action_image); - - passwordContainerView = findViewById(R.id.entry_password_container); - passwordView = findViewById(R.id.entry_password); - passwordActionView = findViewById(R.id.entry_password_action_image); - - urlContainerView = findViewById(R.id.entry_url_container); - urlView = findViewById(R.id.entry_url); - - commentContainerView = findViewById(R.id.entry_notes_container); - commentView = findViewById(R.id.entry_notes); - - extrasView = findViewById(R.id.extra_strings); - - creationDateView = findViewById(R.id.entry_created); - modificationDateView = findViewById(R.id.entry_modified); - lastAccessDateView = findViewById(R.id.entry_accessed); - expiresDateView = findViewById(R.id.entry_expires); - } - - public void applyFontVisibilityToFields(boolean fontInVisibility) { - this.fontInVisibility = fontInVisibility; - } - - public void assignUserName(String userName) { - if (userName != null && !userName.isEmpty()) { - userNameContainerView.setVisibility(VISIBLE); - userNameView.setText(userName); - if (fontInVisibility) - Util.applyFontVisibilityTo(getContext(), userNameView); - } else { - userNameContainerView.setVisibility(GONE); - } - } - - public void assignUserNameCopyListener(OnClickListener onClickListener) { - userNameActionView.setOnClickListener(onClickListener); - } - - public boolean isUserNamePresent() { - return userNameContainerView.getVisibility() == VISIBLE; - } - - public void assignPassword(String password, boolean allowCopyPassword) { - if (password != null && !password.isEmpty()) { - passwordContainerView.setVisibility(VISIBLE); - passwordView.setText(password); - if (fontInVisibility) - Util.applyFontVisibilityTo(getContext(), passwordView); - if (!allowCopyPassword) { - passwordActionView.setColorFilter(ContextCompat.getColor(getContext(), R.color.grey_dark)); - } else { - passwordActionView.setColorFilter(colorAccent); - } - } else { - passwordContainerView.setVisibility(GONE); - } - } - - public void assignPasswordCopyListener(OnClickListener onClickListener) { - if (onClickListener == null) - setClickable(false); - passwordActionView.setOnClickListener(onClickListener); - } - - public boolean isPasswordPresent() { - return passwordContainerView.getVisibility() == VISIBLE; - } - - public boolean atLeastOneFieldProtectedPresent() { - for (int i = 0; i < extrasView.getChildCount(); i++) { - View childCustomView = extrasView.getChildAt(i); - if (childCustomView instanceof EntryCustomFieldProtected) - return true; - } - return false; - } - - public void setHiddenPasswordStyle(boolean hiddenStyle) { - if ( !hiddenStyle ) { - passwordView.setTransformationMethod(null); - } else { - passwordView.setTransformationMethod(PasswordTransformationMethod.getInstance()); - } - // Hidden style for custom fields - for (int i = 0; i < extrasView.getChildCount(); i++) { - View childCustomView = extrasView.getChildAt(i); - if (childCustomView instanceof EntryCustomFieldProtected) - ((EntryCustomFieldProtected) childCustomView).setHiddenPasswordStyle(hiddenStyle); - } - } - - public void assignURL(String url) { - if (url != null && !url.isEmpty()) { - urlContainerView.setVisibility(VISIBLE); - urlView.setText(url); - } else { - urlContainerView.setVisibility(GONE); - } - } - - public void assignComment(String comment) { - if (comment != null && !comment.isEmpty()) { - commentContainerView.setVisibility(VISIBLE); - commentView.setText(comment); - if (fontInVisibility) - Util.applyFontVisibilityTo(getContext(), commentView); - } else { - commentContainerView.setVisibility(GONE); - } - } - - public void addExtraField(String title, ProtectedString value, boolean showAction, OnClickListener onActionClickListener) { - EntryCustomField entryCustomField; - if (value.isProtected()) - entryCustomField = new EntryCustomFieldProtected(getContext(), null, title, value, showAction, onActionClickListener); - else - entryCustomField = new EntryCustomField(getContext(), null, title, value, showAction, onActionClickListener); - entryCustomField.applyFontVisibility(fontInVisibility); - extrasView.addView(entryCustomField); - } - - public void clearExtraFields() { - extrasView.removeAllViews(); - } - - private String getDateTime(Date date) { - return dateFormat.format(date) + " " + timeFormat.format(date); - } - - public void assignCreationDate(Date date) { - creationDateView.setText(getDateTime(date)); - } - - public void assignModificationDate(Date date) { - modificationDateView.setText(getDateTime(date)); - } - - public void assignLastAccessDate(Date date) { - lastAccessDateView.setText(getDateTime(date)); - } - - public void assignExpiresDate(Date date) { - expiresDateView.setText(getDateTime(date)); - } - - public void assignExpiresDate(String constString) { - expiresDateView.setText(constString); - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt new file mode 100644 index 000000000..cc38c8bcf --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt @@ -0,0 +1,250 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + */ +package com.kunzisoft.keepass.view + +import android.content.Context +import android.graphics.Color +import android.support.v4.content.ContextCompat +import android.text.method.PasswordTransformationMethod +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.element.security.ProtectedString +import com.kunzisoft.keepass.utils.Util +import java.text.DateFormat +import java.util.* + +class EntryContentsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) { + + private var fontInVisibility: Boolean = false + private val colorAccent: Int + + private var userNameContainerView: View? = null + private var userNameView: TextView? = null + private var userNameActionView: ImageView? = null + + private var passwordContainerView: View? = null + private var passwordView: TextView? = null + private var passwordActionView: ImageView? = null + + private var urlContainerView: View? = null + private var urlView: TextView? = null + + private var commentContainerView: View? = null + private var commentView: TextView? = null + + private var extrasView: ViewGroup? = null + + private val dateFormat: DateFormat + private val timeFormat: DateFormat + + private var creationDateView: TextView? = null + private var modificationDateView: TextView? = null + private var lastAccessDateView: TextView? = null + private var expiresDateView: TextView? = null + + val isUserNamePresent: Boolean + get() = userNameContainerView!!.visibility == View.VISIBLE + + val isPasswordPresent: Boolean + get() = passwordContainerView!!.visibility == View.VISIBLE + + init { + + fontInVisibility = false + + dateFormat = android.text.format.DateFormat.getDateFormat(context) + timeFormat = android.text.format.DateFormat.getTimeFormat(context) + + inflate(context) + + val attrColorAccent = intArrayOf(R.attr.colorAccentCompat) + val taColorAccent = context.theme.obtainStyledAttributes(attrColorAccent) + this.colorAccent = taColorAccent.getColor(0, Color.BLACK) + taColorAccent.recycle() + } + + private fun inflate(context: Context) { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + inflater.inflate(R.layout.entry_view_contents, this) + + userNameContainerView = findViewById(R.id.entry_user_name_container) + userNameView = findViewById(R.id.entry_user_name) + userNameActionView = findViewById(R.id.entry_user_name_action_image) + + passwordContainerView = findViewById(R.id.entry_password_container) + passwordView = findViewById(R.id.entry_password) + passwordActionView = findViewById(R.id.entry_password_action_image) + + urlContainerView = findViewById(R.id.entry_url_container) + urlView = findViewById(R.id.entry_url) + + commentContainerView = findViewById(R.id.entry_notes_container) + commentView = findViewById(R.id.entry_notes) + + extrasView = findViewById(R.id.extra_strings) + + creationDateView = findViewById(R.id.entry_created) + modificationDateView = findViewById(R.id.entry_modified) + lastAccessDateView = findViewById(R.id.entry_accessed) + expiresDateView = findViewById(R.id.entry_expires) + } + + fun applyFontVisibilityToFields(fontInVisibility: Boolean) { + this.fontInVisibility = fontInVisibility + } + + fun assignUserName(userName: String?) { + if (userName != null && userName.isNotEmpty()) { + userNameContainerView?.visibility = View.VISIBLE + userNameView?.apply { + text = userName + if (fontInVisibility) + Util.applyFontVisibilityTo(context, this) + } + } else { + userNameContainerView?.visibility = View.GONE + } + } + + fun assignUserNameCopyListener(onClickListener: OnClickListener) { + userNameActionView?.setOnClickListener(onClickListener) + } + + fun assignPassword(password: String?, allowCopyPassword: Boolean) { + if (password != null && password.isNotEmpty()) { + passwordContainerView?.visibility = View.VISIBLE + passwordView?.apply { + text = password + if (fontInVisibility) + Util.applyFontVisibilityTo(context, this) + } + if (!allowCopyPassword) { + passwordActionView?.setColorFilter(ContextCompat.getColor(context, R.color.grey_dark)) + } else { + passwordActionView?.setColorFilter(colorAccent) + } + } else { + passwordContainerView?.visibility = View.GONE + } + } + + fun assignPasswordCopyListener(onClickListener: OnClickListener?) { + if (onClickListener == null) + isClickable = false + passwordActionView?.setOnClickListener(onClickListener) + } + + fun atLeastOneFieldProtectedPresent(): Boolean { + extrasView?.let { + for (i in 0 until it.childCount) { + val childCustomView = it.getChildAt(i) + if (childCustomView is EntryCustomFieldProtected) + return true + } + } + return false + } + + fun setHiddenPasswordStyle(hiddenStyle: Boolean) { + if (!hiddenStyle) { + passwordView?.transformationMethod = null + } else { + passwordView?.transformationMethod = PasswordTransformationMethod.getInstance() + } + // Hidden style for custom fields + extrasView?.let { + for (i in 0 until it.childCount) { + val childCustomView = it.getChildAt(i) + if (childCustomView is EntryCustomFieldProtected) + childCustomView.setHiddenPasswordStyle(hiddenStyle) + } + } + } + + fun assignURL(url: String?) { + if (url != null && url.isNotEmpty()) { + urlContainerView?.visibility = View.VISIBLE + urlView?.text = url + } else { + urlContainerView?.visibility = View.GONE + } + } + + fun assignComment(comment: String?) { + if (comment != null && comment.isNotEmpty()) { + commentContainerView?.visibility = View.VISIBLE + commentView?.apply { + text = comment + if (fontInVisibility) + Util.applyFontVisibilityTo(context, this) + } + + } else { + commentContainerView?.visibility = View.GONE + } + } + + fun addExtraField(title: String, value: ProtectedString, showAction: Boolean, onActionClickListener: OnClickListener) { + val entryCustomField: EntryCustomField + if (value.isProtected) + entryCustomField = EntryCustomFieldProtected(context, null, title, value, showAction, onActionClickListener) + else + entryCustomField = EntryCustomField(context, null, title, value, showAction, onActionClickListener) + entryCustomField.applyFontVisibility(fontInVisibility) + extrasView?.addView(entryCustomField) + } + + fun clearExtraFields() { + extrasView?.removeAllViews() + } + + private fun getDateTime(date: Date): String { + return dateFormat.format(date) + " " + timeFormat.format(date) + } + + fun assignCreationDate(date: Date) { + creationDateView?.text = getDateTime(date) + } + + fun assignModificationDate(date: Date) { + modificationDateView?.text = getDateTime(date) + } + + fun assignLastAccessDate(date: Date) { + lastAccessDateView?.text = getDateTime(date) + } + + fun assignExpiresDate(date: Date) { + expiresDateView?.text = getDateTime(date) + } + + fun assignExpiresDate(constString: String) { + expiresDateView?.text = constString + } + + override fun generateDefaultLayoutParams(): LayoutParams { + return LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + } +} From cc35a1e8aac35bbff4c21168e25f9e6918941d6e Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 18:47:32 +0200 Subject: [PATCH 134/289] Fix deprecated buttons --- .../com/kunzisoft/keepass/activities/EntryActivity.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index afe2d1d2a..04f346e0b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -169,13 +169,13 @@ class EntryActivity : LockingHideActivity() { getString(R.string.clipboard_warning) val warningDialog = AlertDialog.Builder(this@EntryActivity) .setMessage(message).create() - warningDialog.setButton(AlertDialog.BUTTON1, getText(android.R.string.ok) + warningDialog.setButton(AlertDialog.BUTTON_POSITIVE, getText(android.R.string.ok) ) { dialog, _ -> PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true) dialog.dismiss() fillEntryDataInContentsView(entry) } - warningDialog.setButton(AlertDialog.BUTTON2, getText(android.R.string.cancel) + warningDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel) ) { dialog, _ -> PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false) dialog.dismiss() @@ -328,7 +328,9 @@ class EntryActivity : LockingHideActivity() { } R.id.menu_edit -> { - EntryEditActivity.launch(this@EntryActivity, mEntry) + mEntry?.let { + EntryEditActivity.launch(this@EntryActivity, it) + } return true } From 191e0cc654a26e36023cda525e13c9531c85e209 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 18:48:56 +0200 Subject: [PATCH 135/289] Kotlinized EntryEditActivity --- .../keepass/activities/EntryEditActivity.java | 567 ------------------ .../keepass/activities/EntryEditActivity.kt | 542 +++++++++++++++++ .../keepass/activities/GroupActivity.java | 4 +- .../activities/lock/LockingActivity.kt | 4 +- 4 files changed, 546 insertions(+), 571 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java deleted file mode 100644 index 5ee760dcb..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.java +++ /dev/null @@ -1,567 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.activities; - -import android.app.Activity; -import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.os.Bundle; -import android.os.Handler; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.Toast; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.lock.LockingHideActivity; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.action.node.ActionNodeValues; -import com.kunzisoft.keepass.database.action.node.AddEntryRunnable; -import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; -import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.EntryVersioned; -import com.kunzisoft.keepass.database.element.GroupVersioned; -import com.kunzisoft.keepass.database.element.PwDate; -import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwIconStandard; -import com.kunzisoft.keepass.database.element.PwNodeId; -import com.kunzisoft.keepass.database.element.security.ProtectedString; -import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment; -import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; -import com.kunzisoft.keepass.education.EntryEditActivityEducation; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.tasks.ActionRunnable; -import com.kunzisoft.keepass.timeout.TimeoutHelper; -import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.utils.Util; -import com.kunzisoft.keepass.view.EntryEditCustomField; - -import org.jetbrains.annotations.NotNull; - -import static com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD; - -public class EntryEditActivity extends LockingHideActivity - implements IconPickerDialogFragment.IconPickerListener, - GeneratePasswordDialogFragment.GeneratePasswordListener { - - private static final String TAG = EntryEditActivity.class.getName(); - - // Keys for current Activity - public static final String KEY_ENTRY = "entry"; - public static final String KEY_PARENT = "parent"; - - // Keys for callback - public static final int ADD_ENTRY_RESULT_CODE = 31; - public static final int UPDATE_ENTRY_RESULT_CODE = 32; - public static final int ADD_OR_UPDATE_ENTRY_REQUEST_CODE = 7129; - public static final String ADD_OR_UPDATE_ENTRY_KEY = "ADD_OR_UPDATE_ENTRY_KEY"; - - private Database database; - - protected EntryVersioned mEntry; - protected GroupVersioned mParent; - protected EntryVersioned mNewEntry; - protected boolean mIsNew; - protected PwIconStandard mSelectedIconStandard; - - // Views - private ScrollView scrollView; - private EditText entryTitleView; - private ImageView entryIconView; - private EditText entryUserNameView; - private EditText entryUrlView; - private EditText entryPasswordView; - private EditText entryConfirmationPasswordView; - private View generatePasswordView; - private EditText entryCommentView; - private ViewGroup entryExtraFieldsContainer; - private View addNewFieldView; - private View saveView; - private int iconColor; - - // Education - private EntryEditActivityEducation entryEditActivityEducation; - - /** - * Launch EntryEditActivity to update an existing entry - * - * @param activity from activity - * @param pwEntry Entry to update - */ - public static void launch(Activity activity, EntryVersioned pwEntry) { - if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { - Intent intent = new Intent(activity, EntryEditActivity.class); - intent.putExtra(KEY_ENTRY, pwEntry.getNodeId()); - activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); - } - } - - /** - * Launch EntryEditActivity to add a new entry - * - * @param activity from activity - * @param pwGroup Group who will contains new entry - */ - public static void launch(Activity activity, GroupVersioned pwGroup) { - if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { - Intent intent = new Intent(activity, EntryEditActivity.class); - intent.putExtra(KEY_PARENT, pwGroup.getNodeId()); - activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.entry_edit); - - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - assert getSupportActionBar() != null; - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - - scrollView = findViewById(R.id.entry_edit_scroll); - scrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); - - entryTitleView = findViewById(R.id.entry_edit_title); - entryIconView = findViewById(R.id.entry_edit_icon_button); - entryUserNameView = findViewById(R.id.entry_edit_user_name); - entryUrlView = findViewById(R.id.entry_edit_url); - entryPasswordView = findViewById(R.id.entry_edit_password); - entryConfirmationPasswordView = findViewById(R.id.entry_edit_confirmation_password); - entryCommentView = findViewById(R.id.entry_edit_notes); - entryExtraFieldsContainer = findViewById(R.id.entry_edit_advanced_container); - - // Focus view to reinitialize timeout - resetAppTimeoutWhenViewFocusedOrChanged( - entryTitleView, - entryIconView, - entryUserNameView, - entryUrlView, - entryPasswordView, - entryConfirmationPasswordView, - entryCommentView, - entryExtraFieldsContainer); - - // Likely the app has been killed exit the activity - database = App.Companion.getCurrentDatabase(); - - // Retrieve the textColor to tint the icon - int[] attrs = {android.R.attr.textColorPrimary}; - TypedArray ta = getTheme().obtainStyledAttributes(attrs); - iconColor = ta.getColor(0, Color.WHITE); - - mSelectedIconStandard = database.getIconFactory().getUnknownIcon(); - - Intent intent = getIntent(); - // Entry is retrieve, it's an entry to update - PwNodeId keyEntry = intent.getParcelableExtra(KEY_ENTRY); - if (keyEntry != null) { - mIsNew = false; - mEntry = database.getEntryById(keyEntry); - if (mEntry != null) { - mParent = mEntry.getParent(); - fillData(); - } - } - - // Parent is retrieve, it's a new entry to create - PwNodeId keyParent = intent.getParcelableExtra(KEY_PARENT); - if (keyParent != null) { - mIsNew = true; - mEntry = database.createEntry(); - mParent = database.getGroupById(keyParent); - // Add the default icon - database.getDrawFactory().assignDefaultDatabaseIconTo(this, entryIconView, iconColor); - } - - // Close the activity if entry or parent can't be retrieve - if (mEntry == null || mParent == null) { - finish(); - return; - } - - // Assign title - setTitle((mIsNew) ? getString(R.string.add_entry) : getString(R.string.edit_entry)); - - // Retrieve the icon after an orientation change - if (savedInstanceState != null - && savedInstanceState.containsKey(KEY_ICON_STANDARD)) { - iconPicked(savedInstanceState); - } - - // Add listener to the icon - entryIconView.setOnClickListener(v -> - IconPickerDialogFragment.launch(EntryEditActivity.this)); - - // Generate password button - generatePasswordView = findViewById(R.id.entry_edit_generate_button); - generatePasswordView.setOnClickListener(v -> openPasswordGenerator()); - - // Save button - saveView = findViewById(R.id.entry_edit_save); - saveView.setOnClickListener(v -> saveEntry()); - - if (mEntry.allowExtraFields()) { - addNewFieldView = findViewById(R.id.entry_edit_add_new_field); - addNewFieldView.setVisibility(View.VISIBLE); - addNewFieldView.setOnClickListener(v -> addNewCustomField()); - } - - // Verify the education views - entryEditActivityEducation = new EntryEditActivityEducation(this); - new Handler().post(() -> performedNextEducation(entryEditActivityEducation)); - } - - private void performedNextEducation(EntryEditActivityEducation entryEditActivityEducation) { - if (entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation( - generatePasswordView, - tapTargetView -> { - openPasswordGenerator(); - return null; - }, - tapTargetView -> { - performedNextEducation(entryEditActivityEducation); - return null; - } - )); - else if (mEntry.allowExtraFields() - && !mEntry.containsCustomFields() - && entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation( - addNewFieldView, - tapTargetView -> { - addNewCustomField(); - return null; - }, - tapTargetView -> null) - ); - } - - /** - * Open the password generator fragment - */ - private void openPasswordGenerator() { - GeneratePasswordDialogFragment generatePasswordDialogFragment = new GeneratePasswordDialogFragment(); - generatePasswordDialogFragment.show(getSupportFragmentManager(), "PasswordGeneratorFragment"); - } - - /** - * Add a new view to fill in the information of the customized field - */ - private void addNewCustomField() { - EntryEditCustomField entryEditCustomField = new EntryEditCustomField(EntryEditActivity.this); - entryEditCustomField.setData("", new ProtectedString(false, "")); - boolean visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this); - entryEditCustomField.setFontVisibility(visibilityFontActivated); - entryExtraFieldsContainer.addView(entryEditCustomField); - - // Scroll bottom - scrollView.post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN)); - } - - /** - * Saves the new entry or update an existing entry in the database - */ - private void saveEntry() { - if (!validateBeforeSaving()) { - return; - } - // Clone the entry - mNewEntry = new EntryVersioned(mEntry); - - populateEntryWithViewInfo(mNewEntry); - - // Open a progress dialog and save entry - ActionRunnable task; - AfterActionNodeFinishRunnable afterActionNodeFinishRunnable = - new AfterActionNodeFinishRunnable() { - @Override - public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { - if (actionNodeValues.getSuccess()) - finish(); - } - }; - if ( mIsNew ) { - task = new AddEntryRunnable(EntryEditActivity.this, - database, - mNewEntry, - mParent, - afterActionNodeFinishRunnable, - !getReadOnly()); - } else { - task = new UpdateEntryRunnable(EntryEditActivity.this, - database, - mEntry, - mNewEntry, - afterActionNodeFinishRunnable, - !getReadOnly()); - } - new Thread(task).start(); - } - - - - /** - * Utility class to retrieve a validation or an error with a message - */ - private class ErrorValidation { - static final int unknownMessage = -1; - - boolean isValidate = false; - int messageId = unknownMessage; - - void showValidationErrorIfNeeded() { - if (!isValidate && messageId != unknownMessage) - Toast.makeText(EntryEditActivity.this, messageId, Toast.LENGTH_LONG).show(); - } - } - - /** - * Validate or not the entry form - * - * @return ErrorValidation An error with a message or a validation without message - */ - protected ErrorValidation validate() { - ErrorValidation errorValidation = new ErrorValidation(); - - // Require title - String title = entryTitleView.getText().toString(); - if ( title.length() == 0 ) { - errorValidation.messageId = R.string.error_title_required; - return errorValidation; - } - - // Validate password - String pass = entryPasswordView.getText().toString(); - String conf = entryConfirmationPasswordView.getText().toString(); - if ( ! pass.equals(conf) ) { - errorValidation.messageId = R.string.error_pass_match; - return errorValidation; - } - - // Validate extra fields - if (mEntry.allowExtraFields()) { - for (int i = 0; i < entryExtraFieldsContainer.getChildCount(); i++) { - EntryEditCustomField entryEditCustomField = (EntryEditCustomField) entryExtraFieldsContainer.getChildAt(i); - String key = entryEditCustomField.getLabel(); - if (key == null || key.length() == 0) { - errorValidation.messageId = R.string.error_string_key; - return errorValidation; - } - } - } - - errorValidation.isValidate = true; - return errorValidation; - } - - /** - * Launch a validation with {@link #validate()} and show the error if present - * - * @return true if the form was validate or false if not - */ - protected boolean validateBeforeSaving() { - ErrorValidation errorValidation = validate(); - errorValidation.showValidationErrorIfNeeded(); - return errorValidation.isValidate; - } - - private void populateEntryWithViewInfo(EntryVersioned newEntry) { - - database.startManageEntry(newEntry); - - newEntry.setLastAccessTime(new PwDate()); - newEntry.setLastModificationTime(new PwDate()); - - newEntry.setTitle(entryTitleView.getText().toString()); - newEntry.setIcon(retrieveIcon()); - - newEntry.setUrl(entryUrlView.getText().toString()); - newEntry.setUsername(entryUserNameView.getText().toString()); - newEntry.setNotes(entryCommentView.getText().toString()); - newEntry.setPassword(entryPasswordView.getText().toString()); - - if (newEntry.allowExtraFields()) { - // Delete all extra strings - newEntry.removeAllCustomFields(); - // Add extra fields from views - for (int i = 0; i < entryExtraFieldsContainer.getChildCount(); i++) { - EntryEditCustomField view = (EntryEditCustomField) entryExtraFieldsContainer.getChildAt(i); - String key = view.getLabel(); - String value = view.getValue(); - boolean protect = view.isProtected(); - newEntry.addExtraField(key, new ProtectedString(protect, value)); - } - } - - database.stopManageEntry(newEntry); - } - - /** - * Retrieve the icon by the selection, or the first icon in the list if the entry is new or the last one - */ - private PwIcon retrieveIcon() { - - if (!mSelectedIconStandard.isUnknown()) - return mSelectedIconStandard; - else { - if (mIsNew) { - return database.getIconFactory().getKeyIcon(); - } - else { - // Keep previous icon, if no new one was selected - return mEntry.getIcon(); - } - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.database_lock, menu); - MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); - - return true; - } - - public boolean onOptionsItemSelected(MenuItem item) { - switch ( item.getItemId() ) { - case R.id.menu_lock: - lockAndExit(); - return true; - - case R.id.menu_contribute: - return MenuUtil.INSTANCE.onContributionItemSelected(this); - - case android.R.id.home: - finish(); - } - - return super.onOptionsItemSelected(item); - } - - private void assignIconView() { - database.getDrawFactory() - .assignDatabaseIconTo( - this, - entryIconView, - mEntry.getIcon(), - iconColor); - } - - protected void fillData() { - - assignIconView(); - - // Don't start the field reference manager, we want to see the raw ref - App.Companion.getCurrentDatabase().stopManageEntry(mEntry); - - entryTitleView.setText(mEntry.getTitle()); - entryUserNameView.setText(mEntry.getUsername()); - entryUrlView.setText(mEntry.getUrl()); - String password = mEntry.getPassword(); - entryPasswordView.setText(password); - entryConfirmationPasswordView.setText(password); - entryCommentView.setText(mEntry.getNotes()); - - boolean visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this); - if (visibilityFontActivated) { - Util.applyFontVisibilityTo(this, entryUserNameView); - Util.applyFontVisibilityTo(this, entryPasswordView); - Util.applyFontVisibilityTo(this, entryConfirmationPasswordView); - Util.applyFontVisibilityTo(this, entryCommentView); - } - - if (mEntry.allowExtraFields()) { - LinearLayout container = findViewById(R.id.entry_edit_advanced_container); - mEntry.getFields().doActionToAllCustomProtectedField((key, value) -> { - EntryEditCustomField entryEditCustomField = new EntryEditCustomField(EntryEditActivity.this); - entryEditCustomField.setData(key, value); - entryEditCustomField.setFontVisibility(visibilityFontActivated); - container.addView(entryEditCustomField); - return null; - }); - } - } - - @Override - public void iconPicked(Bundle bundle) { - mSelectedIconStandard = bundle.getParcelable(KEY_ICON_STANDARD); - mEntry.setIcon(mSelectedIconStandard); - assignIconView(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - if (!mSelectedIconStandard.isUnknown()) { - outState.putParcelable(KEY_ICON_STANDARD, mSelectedIconStandard); - super.onSaveInstanceState(outState); - } - } - - @Override - public void acceptPassword(Bundle bundle) { - String generatedPassword = bundle.getString(GeneratePasswordDialogFragment.KEY_PASSWORD_ID); - entryPasswordView.setText(generatedPassword); - entryConfirmationPasswordView.setText(generatedPassword); - - new Handler().post(() -> performedNextEducation(entryEditActivityEducation)); - } - - @Override - public void cancelPassword(Bundle bundle) { - // Do nothing here - } - - @Override - public void finish() { - // Assign entry callback as a result in all case - try { - if (mNewEntry != null) { - Bundle bundle = new Bundle(); - Intent intentEntry = new Intent(); - bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, mNewEntry); - intentEntry.putExtras(bundle); - if (mIsNew) { - setResult(ADD_ENTRY_RESULT_CODE, intentEntry); - } else { - setResult(UPDATE_ENTRY_RESULT_CODE, intentEntry); - } - } - super.finish(); - } catch (Exception e) { - // Exception when parcelable can't be done - Log.e(TAG, "Cant add entry as result", e); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt new file mode 100644 index 000000000..4797af08b --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -0,0 +1,542 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + */ +package com.kunzisoft.keepass.activities + +import android.app.Activity +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.os.Handler +import android.support.v7.widget.Toolbar +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.ScrollView +import android.widget.Toast + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.lock.LockingHideActivity +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.action.node.ActionNodeValues +import com.kunzisoft.keepass.database.action.node.AddEntryRunnable +import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable +import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.GroupVersioned +import com.kunzisoft.keepass.database.element.PwDate +import com.kunzisoft.keepass.database.element.PwIcon +import com.kunzisoft.keepass.database.element.PwIconStandard +import com.kunzisoft.keepass.database.element.PwNodeId +import com.kunzisoft.keepass.database.element.security.ProtectedString +import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment +import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment +import com.kunzisoft.keepass.education.EntryEditActivityEducation +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.timeout.TimeoutHelper +import com.kunzisoft.keepass.utils.MenuUtil +import com.kunzisoft.keepass.utils.Util +import com.kunzisoft.keepass.view.EntryEditCustomField + +import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD + +class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPickerListener, GeneratePasswordDialogFragment.GeneratePasswordListener { + + private var mDatabase: Database? = null + + private var mEntry: EntryVersioned? = null + private var mParent: GroupVersioned? = null + private var mNewEntry: EntryVersioned? = null + private var mIsNew: Boolean = false + private var mSelectedIconStandard: PwIconStandard? = null + + // Views + private var scrollView: ScrollView? = null + private var entryTitleView: EditText? = null + private var entryIconView: ImageView? = null + private var entryUserNameView: EditText? = null + private var entryUrlView: EditText? = null + private var entryPasswordView: EditText? = null + private var entryConfirmationPasswordView: EditText? = null + private var generatePasswordView: View? = null + private var entryCommentView: EditText? = null + private var entryExtraFieldsContainer: ViewGroup? = null + private var addNewFieldView: View? = null + private var saveView: View? = null + private var iconColor: Int = 0 + + // View validation message + private var validationErrorMessageId = UNKNOWN_MESSAGE + + // Education + private var entryEditActivityEducation: EntryEditActivityEducation? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.entry_edit) + + val toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + + scrollView = findViewById(R.id.entry_edit_scroll) + scrollView?.scrollBarStyle = View.SCROLLBARS_INSIDE_INSET + + entryTitleView = findViewById(R.id.entry_edit_title) + entryIconView = findViewById(R.id.entry_edit_icon_button) + entryUserNameView = findViewById(R.id.entry_edit_user_name) + entryUrlView = findViewById(R.id.entry_edit_url) + entryPasswordView = findViewById(R.id.entry_edit_password) + entryConfirmationPasswordView = findViewById(R.id.entry_edit_confirmation_password) + entryCommentView = findViewById(R.id.entry_edit_notes) + entryExtraFieldsContainer = findViewById(R.id.entry_edit_advanced_container) + + // Focus view to reinitialize timeout + resetAppTimeoutWhenViewFocusedOrChanged( + entryTitleView, + entryIconView, + entryUserNameView, + entryUrlView, + entryPasswordView, + entryConfirmationPasswordView, + entryCommentView, + entryExtraFieldsContainer) + + // Likely the app has been killed exit the activity + mDatabase = App.currentDatabase + + // Retrieve the textColor to tint the icon + iconColor = theme + .obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary)) + .getColor(0, Color.WHITE) + + mSelectedIconStandard = mDatabase?.iconFactory?.unknownIcon + + // Entry is retrieve, it's an entry to update + intent.getParcelableExtra>(KEY_ENTRY)?.let { + mIsNew = false + mEntry = mDatabase?.getEntryById(it) + mEntry?.let { entry -> + mParent = entry.parent + fillEntryDataInContentsView(entry) + } + } + + // Parent is retrieve, it's a new entry to create + intent.getParcelableExtra>(KEY_PARENT)?.let { + mIsNew = true + mEntry = mDatabase?.createEntry() + mParent = mDatabase?.getGroupById(it) + // Add the default icon + mDatabase?.drawFactory?.assignDefaultDatabaseIconTo(this, entryIconView, iconColor) + } + + // Close the activity if entry or parent can't be retrieve + if (mEntry == null || mParent == null) { + finish() + return + } + + // Assign title + title = if (mIsNew) getString(R.string.add_entry) else getString(R.string.edit_entry) + + // Retrieve the icon after an orientation change + savedInstanceState?.let { + if (it.containsKey(KEY_ICON_STANDARD)) { + iconPicked(it) + } + } + + // Add listener to the icon + entryIconView?.setOnClickListener { IconPickerDialogFragment.launch(this@EntryEditActivity) } + + // Generate password button + generatePasswordView = findViewById(R.id.entry_edit_generate_button) + generatePasswordView?.setOnClickListener { openPasswordGenerator() } + + // Save button + saveView = findViewById(R.id.entry_edit_save) + mEntry?.let { entry -> + saveView?.setOnClickListener { saveEntry(entry) } + } + + if (mEntry?.allowExtraFields() == true) { + addNewFieldView = findViewById(R.id.entry_edit_add_new_field) + addNewFieldView?.apply { + visibility = View.VISIBLE + setOnClickListener { addNewCustomField() } + } + } + + // Verify the education views + entryEditActivityEducation = EntryEditActivityEducation(this) + entryEditActivityEducation?.let { + Handler().post { performedNextEducation(it) } + } + } + + private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) { + if (generatePasswordView != null + && entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation( + generatePasswordView!!, + { + openPasswordGenerator() + }, + { + performedNextEducation(entryEditActivityEducation) + } + )) + else if (mEntry != null + && mEntry!!.allowExtraFields() + && !mEntry!!.containsCustomFields() + && addNewFieldView != null + && entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation( + addNewFieldView!!, + { + addNewCustomField() + })) + ; + } + + /** + * Open the password generator fragment + */ + private fun openPasswordGenerator() { + GeneratePasswordDialogFragment().show(supportFragmentManager, "PasswordGeneratorFragment") + } + + /** + * Add a new view to fill in the information of the customized field + */ + private fun addNewCustomField() { + val entryEditCustomField = EntryEditCustomField(this@EntryEditActivity) + entryEditCustomField.setData("", ProtectedString(false, "")) + val visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this) + entryEditCustomField.setFontVisibility(visibilityFontActivated) + entryExtraFieldsContainer?.addView(entryEditCustomField) + + // Scroll bottom + scrollView?.post { scrollView?.fullScroll(ScrollView.FOCUS_DOWN) } + } + + /** + * Saves the new entry or update an existing entry in the database + */ + private fun saveEntry(entry: EntryVersioned) { + + // Launch a validation and show the error if present + if (!isValid()) { + if (validationErrorMessageId != UNKNOWN_MESSAGE) + Toast.makeText(this@EntryEditActivity, validationErrorMessageId, Toast.LENGTH_LONG).show() + return + } + // Clone the entry + mDatabase?.let { database -> + mNewEntry = EntryVersioned(entry) + mNewEntry?.let { newEntry -> + populateEntryWithViewInfo(newEntry) + + // Open a progress dialog and save entry + var task: ActionRunnable? = null + val afterActionNodeFinishRunnable = object : AfterActionNodeFinishRunnable() { + override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { + if (actionNodeValues.success) + finish() + } + } + if (mIsNew) { + mParent?.let { parent -> + task = AddEntryRunnable(this@EntryEditActivity, + database, + newEntry, + parent, + afterActionNodeFinishRunnable, + !readOnly) + } + + } else { + task = UpdateEntryRunnable(this@EntryEditActivity, + database, + entry, + newEntry, + afterActionNodeFinishRunnable, + !readOnly) + } + Thread(task).start() + } + } + } + + /** + * Validate or not the entry form + * + * @return ErrorValidation An error with a message or a validation without message + */ + private fun isValid(): Boolean { + + // Require title + if (entryTitleView?.text.toString().isEmpty()) { + validationErrorMessageId = R.string.error_title_required + return false + } + + // Validate password + if (entryPasswordView?.text.toString() != entryConfirmationPasswordView?.text.toString()) { + validationErrorMessageId = R.string.error_pass_match + return false + } + + // Validate extra fields + if (mEntry?.allowExtraFields() == true) { + entryExtraFieldsContainer?.let { + for (i in 0 until it.childCount) { + val entryEditCustomField = it.getChildAt(i) as EntryEditCustomField + val key = entryEditCustomField.label + if (key == null || key.isEmpty()) { + validationErrorMessageId = R.string.error_string_key + return false + } + } + } + } + return true + } + + private fun populateEntryWithViewInfo(newEntry: EntryVersioned) { + + mDatabase?.startManageEntry(newEntry) + + newEntry.lastAccessTime = PwDate() + newEntry.lastModificationTime = PwDate() + + newEntry.title = entryTitleView?.text.toString() + newEntry.icon = retrieveIcon() + + newEntry.url = entryUrlView?.text.toString() + newEntry.username = entryUserNameView?.text.toString() + newEntry.notes = entryCommentView?.text.toString() + newEntry.password = entryPasswordView?.text.toString() + + if (newEntry.allowExtraFields()) { + // Delete all extra strings + newEntry.removeAllCustomFields() + // Add extra fields from views + entryExtraFieldsContainer?.let { + for (i in 0 until it.childCount) { + val view = it.getChildAt(i) as EntryEditCustomField + val key = view.label + val value = view.value + val protect = view.isProtected + newEntry.addExtraField(key, ProtectedString(protect, value)) + } + } + } + + mDatabase?.stopManageEntry(newEntry) + } + + /** + * Retrieve the icon by the selection, or the first icon in the list if the entry is new or the last one + */ + private fun retrieveIcon(): PwIcon { + + return if (mSelectedIconStandard?.isUnknown != true) + mSelectedIconStandard + else { + if (mIsNew) { + mDatabase?.iconFactory?.keyIcon + } else { + // Keep previous icon, if no new one was selected + mEntry?.icon + } + } ?: PwIconStandard() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + + val inflater = menuInflater + inflater.inflate(R.menu.database_lock, menu) + MenuUtil.contributionMenuInflater(inflater, menu) + + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menu_lock -> { + lockAndExit() + return true + } + + R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this) + + android.R.id.home -> finish() + } + + return super.onOptionsItemSelected(item) + } + + private fun assignIconView() { + mEntry?.icon?.let { + mDatabase?.drawFactory?.assignDatabaseIconTo( + this, + entryIconView, + it, + iconColor) + } + } + + private fun fillEntryDataInContentsView(entry: EntryVersioned) { + + assignIconView() + + // Don't start the field reference manager, we want to see the raw ref + mDatabase?.stopManageEntry(entry) + + entryTitleView?.setText(entry.title) + entryUserNameView?.setText(entry.username) + entryUrlView?.setText(entry.url) + val password = entry.password + entryPasswordView?.setText(password) + entryConfirmationPasswordView?.setText(password) + entryCommentView?.setText(entry.notes) + + val visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this) + if (visibilityFontActivated) { + Util.applyFontVisibilityTo(this, entryUserNameView) + Util.applyFontVisibilityTo(this, entryPasswordView) + Util.applyFontVisibilityTo(this, entryConfirmationPasswordView) + Util.applyFontVisibilityTo(this, entryCommentView) + } + + if (entry.allowExtraFields()) { + val container = findViewById(R.id.entry_edit_advanced_container) + entry.fields.doActionToAllCustomProtectedField { key, value -> + val entryEditCustomField = EntryEditCustomField(this@EntryEditActivity) + entryEditCustomField.setData(key, value) + entryEditCustomField.setFontVisibility(visibilityFontActivated) + container.addView(entryEditCustomField) + } + } + } + + override fun iconPicked(bundle: Bundle) { + mSelectedIconStandard = bundle.getParcelable(KEY_ICON_STANDARD) + mSelectedIconStandard?.let { + mEntry?.icon = it + } + assignIconView() + } + + override fun onSaveInstanceState(outState: Bundle) { + if (!mSelectedIconStandard!!.isUnknown) { + outState.putParcelable(KEY_ICON_STANDARD, mSelectedIconStandard) + super.onSaveInstanceState(outState) + } + } + + override fun acceptPassword(bundle: Bundle) { + bundle.getString(GeneratePasswordDialogFragment.KEY_PASSWORD_ID)?.let { + entryPasswordView?.setText(it) + entryConfirmationPasswordView?.setText(it) + } + + entryEditActivityEducation?.let { + Handler().post { performedNextEducation(it) } + } + } + + override fun cancelPassword(bundle: Bundle) { + // Do nothing here + } + + override fun finish() { + // Assign entry callback as a result in all case + try { + mNewEntry?.let { + val bundle = Bundle() + val intentEntry = Intent() + bundle.putParcelable(ADD_OR_UPDATE_ENTRY_KEY, mNewEntry) + intentEntry.putExtras(bundle) + if (mIsNew) { + setResult(ADD_ENTRY_RESULT_CODE, intentEntry) + } else { + setResult(UPDATE_ENTRY_RESULT_CODE, intentEntry) + } + } + super.finish() + } catch (e: Exception) { + // Exception when parcelable can't be done + Log.e(TAG, "Cant add entry as result", e) + } + } + + companion object { + + private val TAG = EntryEditActivity::class.java.name + + // Keys for current Activity + const val KEY_ENTRY = "entry" + const val KEY_PARENT = "parent" + + // Keys for callback + const val ADD_ENTRY_RESULT_CODE = 31 + const val UPDATE_ENTRY_RESULT_CODE = 32 + const val ADD_OR_UPDATE_ENTRY_REQUEST_CODE = 7129 + const val ADD_OR_UPDATE_ENTRY_KEY = "ADD_OR_UPDATE_ENTRY_KEY" + + const val UNKNOWN_MESSAGE = -1 + + /** + * Launch EntryEditActivity to update an existing entry + * + * @param activity from activity + * @param pwEntry Entry to update + */ + fun launch(activity: Activity, pwEntry: EntryVersioned) { + if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { + val intent = Intent(activity, EntryEditActivity::class.java) + intent.putExtra(KEY_ENTRY, pwEntry.nodeId) + activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE) + } + } + + /** + * Launch EntryEditActivity to add a new entry + * + * @param activity from activity + * @param pwGroup Group who will contains new entry + */ + fun launch(activity: Activity, pwGroup: GroupVersioned) { + if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { + val intent = Intent(activity, EntryEditActivity::class.java) + intent.putExtra(KEY_PARENT, pwGroup.nodeId) + activity.startActivityForResult(intent, ADD_OR_UPDATE_ENTRY_REQUEST_CODE) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 4a0266b63..931019011 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -298,7 +298,7 @@ public class GroupActivity extends LockingActivity .show(getSupportFragmentManager(), GroupEditDialogFragment.TAG_CREATE_GROUP)); addNodeButtonView.setAddEntryClickListener(v -> - EntryEditActivity.launch(GroupActivity.this, mCurrentGroup)); + EntryEditActivity.Companion.launch(GroupActivity.this, mCurrentGroup)); // Search suggestion searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database); @@ -570,7 +570,7 @@ public class GroupActivity extends LockingActivity GroupEditDialogFragment.TAG_CREATE_GROUP); break; case ENTRY: - EntryEditActivity.launch(GroupActivity.this, (EntryVersioned) node); + EntryEditActivity.Companion.launch(GroupActivity.this, (EntryVersioned) node); break; } return true; diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index 6849211e3..dfd61bc85 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -163,9 +163,9 @@ abstract class LockingActivity : StylishActivity() { /** * To reset the app timeout when a view is focused or changed */ - protected fun resetAppTimeoutWhenViewFocusedOrChanged(vararg views: View) { + protected fun resetAppTimeoutWhenViewFocusedOrChanged(vararg views: View?) { views.forEach { - it.setOnFocusChangeListener { _, hasFocus -> + it?.setOnFocusChangeListener { _, hasFocus -> if (hasFocus) { TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) } From eda7fa0fe79e24beeb89a40451bb99292f42cbaa Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 18:49:37 +0200 Subject: [PATCH 136/289] Fix NodeHandler parameter --- .../com/kunzisoft/keepass/database/search/SearchDbHelper.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt index ee3445b34..f10d00120 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchDbHelper.kt @@ -48,11 +48,11 @@ class SearchDbHelper(private val isOmitBackup: Boolean) { incrementEntry = 0 database.rootGroup?.doForEachChild( object : NodeHandler() { - override fun operate(entry: EntryVersioned): Boolean { + override fun operate(node: EntryVersioned): Boolean { if (incrementEntry >= max) return false - if (entryContainsString(entry, finalQStr, loc)) { - searchGroup?.addChildEntry(entry) + if (entryContainsString(node, finalQStr, loc)) { + searchGroup?.addChildEntry(node) incrementEntry++ } // Stop searching when we have max entries From d6bac74e1b6409925e2f8d050ed8755df0348fac Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 20:01:17 +0200 Subject: [PATCH 137/289] Kotlinized GroupActivity --- .../keepass/activities/GroupActivity.java | 1135 ----------------- .../keepass/activities/GroupActivity.kt | 1051 +++++++++++++++ .../AssignPasswordInDatabaseRunnable.kt | 5 +- .../FileDatabaseSelectActivity.java | 6 +- .../IntentBuildLauncher.java | 2 +- .../keepass/password/PasswordActivity.java | 7 +- 6 files changed, 1060 insertions(+), 1146 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt rename app/src/main/java/com/kunzisoft/keepass/{activities => password}/IntentBuildLauncher.java (73%) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java deleted file mode 100644 index 931019011..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ /dev/null @@ -1,1135 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.activities; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Dialog; -import android.app.SearchManager; -import android.app.assist.AssistStructure; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresApi; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.widget.SearchView; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.lock.LockingActivity; -import com.kunzisoft.keepass.adapters.NodeAdapter; -import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.autofill.AutofillHelper; -import com.kunzisoft.keepass.database.SortNodeEnum; -import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; -import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; -import com.kunzisoft.keepass.database.action.node.ActionNodeValues; -import com.kunzisoft.keepass.database.action.node.AddGroupRunnable; -import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable; -import com.kunzisoft.keepass.database.action.node.CopyEntryRunnable; -import com.kunzisoft.keepass.database.action.node.DeleteEntryRunnable; -import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable; -import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable; -import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable; -import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.EntryVersioned; -import com.kunzisoft.keepass.database.element.GroupVersioned; -import com.kunzisoft.keepass.database.element.NodeVersioned; -import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwNodeId; -import com.kunzisoft.keepass.database.element.security.ProtectedString; -import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; -import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment; -import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment; -import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; -import com.kunzisoft.keepass.dialogs.ReadOnlyDialog; -import com.kunzisoft.keepass.dialogs.SortDialogFragment; -import com.kunzisoft.keepass.education.GroupActivityEducation; -import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService; -import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; -import com.kunzisoft.keepass.magikeyboard.MagikIME; -import com.kunzisoft.keepass.model.Entry; -import com.kunzisoft.keepass.model.Field; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.timeout.TimeoutHelper; -import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.view.AddNodeButtonView; - -import net.cachapa.expandablelayout.ExpandableLayout; - -import org.jetbrains.annotations.NotNull; - -import kotlin.Unit; -import kotlin.jvm.functions.Function2; - -public class GroupActivity extends LockingActivity - implements GroupEditDialogFragment.EditGroupListener, - IconPickerDialogFragment.IconPickerListener, - NodeAdapter.NodeMenuListener, - ListNodesFragment.OnScrollListener, - AssignMasterKeyDialogFragment.AssignPasswordDialogListener, - NodeAdapter.NodeClickCallback, - SortDialogFragment.SortSelectionListener { - - private static final String TAG = GroupActivity.class.getName(); - - private static final String GROUP_ID_KEY = "GROUP_ID_KEY"; - private static final String LIST_NODES_FRAGMENT_TAG = "LIST_NODES_FRAGMENT_TAG"; - private static final String SEARCH_FRAGMENT_TAG = "SEARCH_FRAGMENT_TAG"; - private static final String OLD_GROUP_TO_UPDATE_KEY = "OLD_GROUP_TO_UPDATE_KEY"; - private static final String NODE_TO_COPY_KEY = "NODE_TO_COPY_KEY"; - private static final String NODE_TO_MOVE_KEY = "NODE_TO_MOVE_KEY"; - - private Toolbar toolbar; - private View searchTitleView; - private ExpandableLayout toolbarPasteExpandableLayout; - private Toolbar toolbarPaste; - private ImageView iconView; - private TextView modeTitleView; - private AddNodeButtonView addNodeButtonView; - private TextView groupNameView; - - private Database database; - - private ListNodesFragment listNodesFragment; - private boolean currentGroupIsASearch; - - private GroupVersioned rootGroup; - private GroupVersioned mCurrentGroup; - private GroupVersioned oldGroupToUpdate; - private NodeVersioned nodeToCopy; - private NodeVersioned nodeToMove; - - private SearchEntryCursorAdapter searchSuggestionAdapter; - - private int iconColor; - - private static void buildAndLaunchIntent(Activity activity, GroupVersioned group, boolean readOnly, - IntentBuildLauncher intentBuildLauncher) { - if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { - Intent intent = new Intent(activity, GroupActivity.class); - if (group != null) { - intent.putExtra(GROUP_ID_KEY, group.getNodeId()); - } - ReadOnlyHelper.INSTANCE.putReadOnlyInIntent(intent, readOnly); - intentBuildLauncher.launchActivity(intent); - } - } - - /* - * ------------------------- - * Standard Launch - * ------------------------- - */ - - public static void launch(Activity activity) { - launch(activity, PreferencesUtil.enableReadOnlyDatabase(activity)); - } - - public static void launch(Activity activity, boolean readOnly) { - launch(activity, null, readOnly); - } - - public static void launch(Activity activity, GroupVersioned group, boolean readOnly) { - TimeoutHelper.INSTANCE.recordTime(activity); - buildAndLaunchIntent(activity, group, readOnly, - (intent) -> activity.startActivityForResult(intent, 0)); - } - - - /* - * ------------------------- - * Keyboard Launch - * ------------------------- - */ - // TODO implement pre search to directly open the direct group - - public static void launchForKeyboardSelection(Activity activity, boolean readOnly) { - TimeoutHelper.INSTANCE.recordTime(activity); - buildAndLaunchIntent(activity, null, readOnly, - (intent) -> KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent)); - } - - /* - * ------------------------- - * Autofill Launch - * ------------------------- - */ - // TODO implement pre search to directly open the direct group - - @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure, boolean readOnly) { - TimeoutHelper.INSTANCE.recordTime(activity); - buildAndLaunchIntent(activity, null, readOnly, - (intent) -> AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, intent, assistStructure)); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (isFinishing()) { - return; - } - - database = App.Companion.getCurrentDatabase(); - - // Construct main view - setContentView(getLayoutInflater().inflate(R.layout.list_nodes_with_add_button, null)); - - // Initialize views - iconView = findViewById(R.id.icon); - addNodeButtonView = findViewById(R.id.add_node_button); - toolbar = findViewById(R.id.toolbar); - searchTitleView = findViewById(R.id.search_title); - groupNameView = findViewById(R.id.group_name); - toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout); - toolbarPaste = findViewById(R.id.toolbar_paste); - modeTitleView = findViewById(R.id.mode_title_view); - - // Focus view to reinitialize timeout - resetAppTimeoutWhenViewFocusedOrChanged(addNodeButtonView); - - // Retrieve elements after an orientation change - if (savedInstanceState != null) { - if (savedInstanceState.containsKey(OLD_GROUP_TO_UPDATE_KEY)) - oldGroupToUpdate = savedInstanceState.getParcelable(OLD_GROUP_TO_UPDATE_KEY); - - if (savedInstanceState.containsKey(NODE_TO_COPY_KEY)) { - nodeToCopy = savedInstanceState.getParcelable(NODE_TO_COPY_KEY); - toolbarPaste.setOnMenuItemClickListener(new OnCopyMenuItemClickListener()); - } - else if (savedInstanceState.containsKey(NODE_TO_MOVE_KEY)) { - nodeToMove = savedInstanceState.getParcelable(NODE_TO_MOVE_KEY); - toolbarPaste.setOnMenuItemClickListener(new OnMoveMenuItemClickListener()); - } - } - - try { - rootGroup = database.getRootGroup(); - } catch (NullPointerException e) { - Log.e(TAG, "Unable to get rootGroup"); - } - mCurrentGroup = retrieveCurrentGroup(getIntent(), savedInstanceState); - currentGroupIsASearch = Intent.ACTION_SEARCH.equals(getIntent().getAction()); - - Log.i(TAG, "Started creating tree"); - if ( mCurrentGroup == null ) { - Log.w(TAG, "Group was null"); - return; - } - - // Update last access time. - mCurrentGroup.touch(false, false); - - toolbar.setTitle(""); - setSupportActionBar(toolbar); - - toolbarPaste.inflateMenu(R.menu.node_paste_menu); - toolbarPaste.setNavigationIcon(R.drawable.ic_arrow_left_white_24dp); - toolbarPaste.setNavigationOnClickListener(view -> { - toolbarPasteExpandableLayout.collapse(); - nodeToCopy = null; - nodeToMove = null; - }); - - // Retrieve the textColor to tint the icon - int[] attrs = {R.attr.textColorInverse}; - TypedArray ta = getTheme().obtainStyledAttributes(attrs); - iconColor = ta.getColor(0, Color.WHITE); - - String fragmentTag = LIST_NODES_FRAGMENT_TAG; - if (currentGroupIsASearch) - fragmentTag = SEARCH_FRAGMENT_TAG; - - // Initialize the fragment with the list - listNodesFragment = (ListNodesFragment) getSupportFragmentManager() - .findFragmentByTag(fragmentTag); - if (listNodesFragment == null) - listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, getReadOnly(), currentGroupIsASearch); - - // Attach fragment to content view - getSupportFragmentManager().beginTransaction().replace( - R.id.nodes_list_fragment_container, - listNodesFragment, - fragmentTag) - .commit(); - - // Add listeners to the add buttons - addNodeButtonView.setAddGroupClickListener(v -> GroupEditDialogFragment.build() - .show(getSupportFragmentManager(), - GroupEditDialogFragment.TAG_CREATE_GROUP)); - addNodeButtonView.setAddEntryClickListener(v -> - EntryEditActivity.Companion.launch(GroupActivity.this, mCurrentGroup)); - - // Search suggestion - searchSuggestionAdapter = new SearchEntryCursorAdapter(this, database); - - Log.i(TAG, "Finished creating tree"); - } - - @Override - protected void onNewIntent(Intent intent) { - Log.d(TAG, "setNewIntent: " + intent.toString()); - setIntent(intent); - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - // only one instance of search in backstack - openSearchGroup(retrieveCurrentGroup(intent, null)); - currentGroupIsASearch = true; - } else { - currentGroupIsASearch = false; - } - } - - private void openSearchGroup(GroupVersioned group) { - // Delete the previous search fragment - Fragment searchFragment = getSupportFragmentManager().findFragmentByTag(SEARCH_FRAGMENT_TAG); - if (searchFragment != null) { - if ( getSupportFragmentManager() - .popBackStackImmediate(SEARCH_FRAGMENT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) ) - getSupportFragmentManager().beginTransaction().remove(searchFragment).commit(); - } - - openGroup(group, true); - } - - private void openChildGroup(GroupVersioned group) { - openGroup(group, false); - } - - private void openGroup(GroupVersioned group, boolean isASearch) { - // Check TimeoutHelper - TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeoutOrResetTimeout(this, () -> { - // Open a group in a new fragment - ListNodesFragment newListNodeFragment = ListNodesFragment.newInstance(group, getReadOnly(), isASearch); - FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); - // Different animation - String fragmentTag; - if (isASearch) { - fragmentTransaction.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom, - R.anim.slide_in_bottom, R.anim.slide_out_top); - fragmentTag = SEARCH_FRAGMENT_TAG; - } else { - fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, - R.anim.slide_in_left, R.anim.slide_out_right); - fragmentTag = LIST_NODES_FRAGMENT_TAG; - } - - fragmentTransaction.replace(R.id.nodes_list_fragment_container, - newListNodeFragment, - fragmentTag); - fragmentTransaction.addToBackStack(fragmentTag); - fragmentTransaction.commit(); - - listNodesFragment = newListNodeFragment; - mCurrentGroup = group; - assignGroupViewElements(); - return null; - }); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - if (mCurrentGroup != null) - outState.putParcelable(GROUP_ID_KEY, mCurrentGroup.getNodeId()); - outState.putParcelable(OLD_GROUP_TO_UPDATE_KEY, oldGroupToUpdate); - if (nodeToCopy != null) - outState.putParcelable(NODE_TO_COPY_KEY, nodeToCopy); - if (nodeToMove != null) - outState.putParcelable(NODE_TO_MOVE_KEY, nodeToMove); - super.onSaveInstanceState(outState); - } - - protected @Nullable GroupVersioned retrieveCurrentGroup(Intent intent, @Nullable Bundle savedInstanceState) { - - // If it's a search - if ( Intent.ACTION_SEARCH.equals(intent.getAction()) ) { - return database.search(intent.getStringExtra(SearchManager.QUERY).trim()); - } - // else a real group - else { - PwNodeId pwGroupId = null; - if (savedInstanceState != null - && savedInstanceState.containsKey(GROUP_ID_KEY)) { - pwGroupId = savedInstanceState.getParcelable(GROUP_ID_KEY); - } else { - if (getIntent() != null) - pwGroupId = intent.getParcelableExtra(GROUP_ID_KEY); - } - - setReadOnly(database.isReadOnly() || getReadOnly()); // Force read only if the database is like that - - Log.w(TAG, "Creating tree view"); - GroupVersioned currentGroup; - if (pwGroupId == null) { - currentGroup = rootGroup; - } else { - currentGroup = database.getGroupById(pwGroupId); - } - - return currentGroup; - } - } - - public void assignGroupViewElements() { - // Assign title - if (mCurrentGroup != null) { - String title = mCurrentGroup.getTitle(); - if (title != null && title.length() > 0) { - if (groupNameView != null) { - groupNameView.setText(title); - groupNameView.invalidate(); - } - } else { - if (groupNameView != null) { - groupNameView.setText(getText(R.string.root)); - groupNameView.invalidate(); - } - } - } - if (currentGroupIsASearch) { - searchTitleView.setVisibility(View.VISIBLE); - } else { - searchTitleView.setVisibility(View.GONE); - } - - // Assign icon - if (currentGroupIsASearch) { - if (toolbar != null) { - toolbar.setNavigationIcon(null); - } - iconView.setVisibility(View.GONE); - } else { - // Assign the group icon depending of IconPack or custom icon - iconView.setVisibility(View.VISIBLE); - if (mCurrentGroup != null) { - database.getDrawFactory().assignDatabaseIconTo(this, iconView, mCurrentGroup.getIcon(), iconColor); - - if (toolbar != null) { - if (mCurrentGroup.containsParent()) - toolbar.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp); - else { - toolbar.setNavigationIcon(null); - } - } - } - } - - // Show selection mode message if needed - if (getSelectionMode()) { - modeTitleView.setVisibility(View.VISIBLE); - } else { - modeTitleView.setVisibility(View.GONE); - } - - // Show button if allowed - if (addNodeButtonView != null) { - - // To enable add button - boolean addGroupEnabled = !getReadOnly() && !currentGroupIsASearch; - boolean addEntryEnabled = !getReadOnly() && !currentGroupIsASearch; - if (mCurrentGroup != null) { - boolean isRoot = (mCurrentGroup != null && mCurrentGroup == rootGroup); - if (!mCurrentGroup.allowAddEntryIfIsRoot()) - addEntryEnabled = !isRoot && addEntryEnabled; - if (isRoot) { - showWarnings(); - } - } - addNodeButtonView.enableAddGroup(addGroupEnabled); - addNodeButtonView.enableAddEntry(addEntryEnabled); - - if (addNodeButtonView.isEnable()) - addNodeButtonView.showButton(); - } - } - - @Override - public void onScrolled(int dy) { - if (addNodeButtonView != null) - addNodeButtonView.hideButtonOnScrollListener(dy); - } - - @Override - public void onNodeClick(NodeVersioned node) { - switch (node.getType()) { - case GROUP: - try { - openChildGroup((GroupVersioned) node); - } catch (ClassCastException e) { - Log.e(TAG, "Node can't be cast in Group"); - } - break; - case ENTRY: - try { - EntryVersioned entry = ((EntryVersioned) node); - EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), - () -> { - EntryActivity.Companion.launch(GroupActivity.this, entry, getReadOnly()); - return null; - }, - () -> { - MagikIME.setEntryKey(getEntry(entry)); - // Show the notification if allowed in Preferences - if (PreferencesUtil.enableKeyboardNotificationEntry(GroupActivity.this)) { - startService(new Intent( - GroupActivity.this, - KeyboardEntryNotificationService.class)); - } - // Consume the selection mode - EntrySelectionHelper.INSTANCE.removeEntrySelectionModeFromIntent(getIntent()); - moveTaskToBack(true); - return null; - }, - assistStructure -> { - // Build response with the entry selected - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.INSTANCE.buildResponseWhenEntrySelected(GroupActivity.this, entry); - } - finish(); - return null; - }); - } catch (ClassCastException e) { - Log.e(TAG, "Node can't be cast in Entry"); - } - break; - } - } - - private Entry getEntry(EntryVersioned entry) { - Entry entryModel = new Entry(); - entryModel.setTitle(entry.getTitle()); - entryModel.setUsername(entry.getUsername()); - entryModel.setPassword(entry.getPassword()); - entryModel.setUrl(entry.getUrl()); - if (entry.containsCustomFields()) { - entry.getFields() - .doActionToAllCustomProtectedField(new Function2() { - @Override - public Unit invoke(String key, ProtectedString value) { - entryModel.addCustomField( - new Field(key, value.toString())); - return null; - } - }); - } - return entryModel; - } - - @Override - public boolean onOpenMenuClick(NodeVersioned node) { - onNodeClick(node); - return true; - } - - @Override - public boolean onEditMenuClick(NodeVersioned node) { - switch (node.getType()) { - case GROUP: - oldGroupToUpdate = (GroupVersioned) node; - GroupEditDialogFragment.build(oldGroupToUpdate) - .show(getSupportFragmentManager(), - GroupEditDialogFragment.TAG_CREATE_GROUP); - break; - case ENTRY: - EntryEditActivity.Companion.launch(GroupActivity.this, (EntryVersioned) node); - break; - } - return true; - } - - @Override - public boolean onCopyMenuClick(NodeVersioned node) { - - toolbarPasteExpandableLayout.expand(); - nodeToCopy = node; - toolbarPaste.setOnMenuItemClickListener(new OnCopyMenuItemClickListener()); - return false; - } - - private class OnCopyMenuItemClickListener implements Toolbar.OnMenuItemClickListener{ - - @Override - public boolean onMenuItemClick(MenuItem item) { - toolbarPasteExpandableLayout.collapse(); - - switch (item.getItemId()) { - case R.id.menu_paste: - switch (nodeToCopy.getType()) { - case GROUP: - Log.e(TAG, "Copy not allowed for group"); - break; - case ENTRY: - copyEntry((EntryVersioned) nodeToCopy, mCurrentGroup); - break; - } - nodeToCopy = null; - return true; - } - return true; - } - } - - private void copyEntry(EntryVersioned entryToCopy, GroupVersioned newParent) { - new Thread(new CopyEntryRunnable(this, - App.Companion.getCurrentDatabase(), - entryToCopy, - newParent, - new AfterAddNodeRunnable(), - !getReadOnly()) - ).start(); - } - - @Override - public boolean onMoveMenuClick(NodeVersioned node) { - - toolbarPasteExpandableLayout.expand(); - nodeToMove = node; - toolbarPaste.setOnMenuItemClickListener(new OnMoveMenuItemClickListener()); - return false; - } - - private class OnMoveMenuItemClickListener implements Toolbar.OnMenuItemClickListener{ - - @Override - public boolean onMenuItemClick(MenuItem item) { - toolbarPasteExpandableLayout.collapse(); - - switch (item.getItemId()) { - case R.id.menu_paste: - switch (nodeToMove.getType()) { - case GROUP: - moveGroup((GroupVersioned) nodeToMove, mCurrentGroup); - break; - case ENTRY: - moveEntry((EntryVersioned) nodeToMove, mCurrentGroup); - break; - } - nodeToMove = null; - return true; - } - return true; - } - } - - private void moveGroup(GroupVersioned groupToMove, GroupVersioned newParent) { - new Thread(new MoveGroupRunnable( - this, - App.Companion.getCurrentDatabase(), - groupToMove, - newParent, - new AfterAddNodeRunnable(), - !getReadOnly()) - ).start(); - } - - private void moveEntry(EntryVersioned entryToMove, GroupVersioned newParent) { - new Thread(new MoveEntryRunnable( - this, - App.Companion.getCurrentDatabase(), - entryToMove, - newParent, - new AfterAddNodeRunnable(), - !getReadOnly()) - ).start(); - } - - @Override - public boolean onDeleteMenuClick(NodeVersioned node) { - switch (node.getType()) { - case GROUP: - deleteGroup((GroupVersioned) node); - break; - case ENTRY: - deleteEntry((EntryVersioned) node); - break; - } - return true; - } - - private void deleteGroup(GroupVersioned group) { - //TODO Verify trash recycle bin - new Thread(new DeleteGroupRunnable( - this, - App.Companion.getCurrentDatabase(), - group, - new AfterDeleteNodeRunnable(), - !getReadOnly()) - ).start(); - } - - private void deleteEntry(EntryVersioned entry) { - new Thread(new DeleteEntryRunnable( - this, - App.Companion.getCurrentDatabase(), - entry, - new AfterDeleteNodeRunnable(), - !getReadOnly()) - ).start(); - } - - @Override - protected void onResume() { - super.onResume(); - // Refresh the elements - assignGroupViewElements(); - // Refresh suggestions to change preferences - if (searchSuggestionAdapter != null) - searchSuggestionAdapter.reInit(this); - } - - @Override - protected void onStop() { - super.onStop(); - // Hide button - if (addNodeButtonView != null) - addNodeButtonView.hideButton(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.search, menu); - inflater.inflate(R.menu.database_lock, menu); - if (!getReadOnly()) - inflater.inflate(R.menu.database_master_key, menu); - if (!getSelectionMode()) { - inflater.inflate(R.menu.default_menu, menu); - MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); - } - - // Get the SearchView and set the searchable configuration - SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); - assert searchManager != null; - - MenuItem searchItem = menu.findItem(R.id.menu_search); - SearchView searchView = null; - if (searchItem != null) { - searchView = (SearchView) searchItem.getActionView(); - } - if (searchView != null) { - searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(this, GroupActivity.class))); - searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default - searchView.setSuggestionsAdapter(searchSuggestionAdapter); - searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { - @Override - public boolean onSuggestionClick(int position) { - onNodeClick(searchSuggestionAdapter.getEntryFromPosition(position)); - return true; - } - - @Override - public boolean onSuggestionSelect(int position) { - return true; - } - }); - } - - super.onCreateOptionsMenu(menu); - - // Launch education screen - new Handler().post(() -> performedNextEducation(new GroupActivityEducation(this), menu)); - - return true; - } - - private void performedNextEducation(GroupActivityEducation groupActivityEducation, - Menu menu) { - // If no node, show education to add new one - if (listNodesFragment != null - && listNodesFragment.isEmpty() - && addNodeButtonView.isEnable() - && groupActivityEducation.checkAndPerformedAddNodeButtonEducation( - addNodeButtonView, - tapTargetView -> { - addNodeButtonView.openButtonIfClose(); - return null; - }, - tapTargetView -> { - performedNextEducation(groupActivityEducation, menu); - return null; - } - )); - else if (toolbar.findViewById(R.id.menu_search) != null - && groupActivityEducation.checkAndPerformedSearchMenuEducation( - toolbar.findViewById(R.id.menu_search), - tapTargetView -> { - menu.findItem(R.id.menu_search).expandActionView(); - return null; - }, - tapTargetView -> { - performedNextEducation(groupActivityEducation, menu); - return null; - })); - else if (toolbar.findViewById(R.id.menu_sort) != null - && groupActivityEducation.checkAndPerformedSortMenuEducation( - toolbar.findViewById(R.id.menu_sort), - tapTargetView -> { - onOptionsItemSelected(menu.findItem(R.id.menu_sort)); - return null; - }, - tapTargetView -> { - performedNextEducation(groupActivityEducation, menu); - return null; - })); - else if (toolbar.findViewById(R.id.menu_lock) != null - && groupActivityEducation.checkAndPerformedLockMenuEducation( - toolbar.findViewById(R.id.menu_lock), - tapTargetView -> { - onOptionsItemSelected(menu.findItem(R.id.menu_lock)); - return null; - }, - tapTargetView -> { - performedNextEducation(groupActivityEducation, menu); - return null; - })); - } - - @Override - public void startActivity(Intent intent) { - - // Get the intent, verify the action and get the query - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - String query = intent.getStringExtra(SearchManager.QUERY); - // manually launch the real search activity - final Intent searchIntent = new Intent(getApplicationContext(), GroupActivity.class); - // add query to the Intent Extras - searchIntent.setAction(Intent.ACTION_SEARCH); - searchIntent.putExtra(SearchManager.QUERY, query); - - EntrySelectionHelper.INSTANCE.doEntrySelectionAction(intent, - () -> { - GroupActivity.super.startActivity(intent); - return null; - }, - () -> { - KeyboardHelper.INSTANCE.startActivityForKeyboardSelection( - GroupActivity.this, - searchIntent); - finish(); - return null; - }, - assistStructure -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.INSTANCE.startActivityForAutofillResult( - GroupActivity.this, - searchIntent, - assistStructure); - } - return null; - }); - } else { - super.startActivity(intent); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - onBackPressed(); - return true; - - case R.id.menu_search: - //onSearchRequested(); - return true; - - case R.id.menu_lock: - lockAndExit(); - return true; - - case R.id.menu_change_master_key: - setPassword(); - return true; - default: - // Check the time lock before launching settings - MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item, getReadOnly(), true); - return super.onOptionsItemSelected(item); - } - } - - private void setPassword() { - AssignMasterKeyDialogFragment dialog = new AssignMasterKeyDialogFragment(); - dialog.show(getSupportFragmentManager(), "passwordDialog"); - } - - @Override - public void approveEditGroup(GroupEditDialogFragment.EditGroupDialogAction action, - String name, - PwIcon icon) { - Database database = App.Companion.getCurrentDatabase(); - - switch (action) { - case CREATION: - // If group creation - // Build the group - GroupVersioned newGroup = database.createGroup(); - newGroup.setTitle(name); - newGroup.setIcon(icon); - // Not really needed here because added in runnable but safe - newGroup.setParent(mCurrentGroup); - - // If group created save it in the database - new Thread(new AddGroupRunnable(this, - App.Companion.getCurrentDatabase(), - newGroup, - mCurrentGroup, - new AfterAddNodeRunnable(), - !getReadOnly()) - ).start(); - - break; - case UPDATE: - // If update add new elements - if (oldGroupToUpdate != null) { - GroupVersioned updateGroup = new GroupVersioned(oldGroupToUpdate); - updateGroup.setTitle(name); - // TODO custom icon - updateGroup.setIcon(icon); - - if (listNodesFragment != null) - listNodesFragment.removeNode(oldGroupToUpdate); - - // If group updated save it in the database - new Thread(new UpdateGroupRunnable(this, - App.Companion.getCurrentDatabase(), - oldGroupToUpdate, - updateGroup, - new AfterUpdateNodeRunnable(), - !getReadOnly()) - ).start(); - } - - break; - } - } - - class AfterAddNodeRunnable extends AfterActionNodeFinishRunnable { - - @Override - public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { - runOnUiThread(() -> { - if (actionNodeValues.getSuccess()) { - if (listNodesFragment != null) - listNodesFragment.addNode(actionNodeValues.getNewNode()); - } - }); - } - } - - class AfterUpdateNodeRunnable extends AfterActionNodeFinishRunnable { - - @Override - public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { - runOnUiThread(() -> { - if (actionNodeValues.getSuccess()) { - if (listNodesFragment != null) - listNodesFragment.updateNode(actionNodeValues.getOldNode(), actionNodeValues.getNewNode()); - } - }); - } - } - - class AfterDeleteNodeRunnable extends AfterActionNodeFinishRunnable { - - @Override - public void onActionNodeFinish(@NotNull ActionNodeValues actionNodeValues) { - runOnUiThread(() -> { - if (actionNodeValues.getSuccess()) { - - if (listNodesFragment != null) - listNodesFragment.removeNode(actionNodeValues.getOldNode()); - - if (actionNodeValues.getOldNode() != null) { - GroupVersioned parent = actionNodeValues.getOldNode().getParent(); - Database database = App.Companion.getCurrentDatabase(); - if (database.isRecycleBinAvailable() && - database.isRecycleBinEnabled()) { - GroupVersioned recycleBin = database.getRecycleBin(); - // Add trash if it doesn't exists - if (parent.equals(recycleBin) - && mCurrentGroup != null - && mCurrentGroup.getParent() == null - && !mCurrentGroup.equals(recycleBin)) { - - if (listNodesFragment != null) - listNodesFragment.addNode(parent); - } - } - } - } - }); - } - } - - @Override - public void cancelEditGroup(GroupEditDialogFragment.EditGroupDialogAction action, - String name, - PwIcon iconId) { - // Do nothing here - } - - @Override - // For icon in create tree dialog - public void iconPicked(Bundle bundle) { - GroupEditDialogFragment groupEditDialogFragment = - (GroupEditDialogFragment) getSupportFragmentManager() - .findFragmentByTag(GroupEditDialogFragment.TAG_CREATE_GROUP); - if (groupEditDialogFragment != null) { - groupEditDialogFragment.iconPicked(bundle); - } - } - - protected void showWarnings() { - if (App.Companion.getCurrentDatabase().isReadOnly()) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - - if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) { - Dialog dialog = new ReadOnlyDialog(this); - dialog.show(); - } - } - } - - @Override - public void onAssignKeyDialogPositiveClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { - - Thread taskThread = new Thread(new ProgressDialogRunnable(this, - R.string.saving_database, - progressTaskUpdater -> { - return new AssignPasswordInDatabaseRunnable(GroupActivity.this, - database, - masterPasswordChecked, - masterPassword, - keyFileChecked, - keyFile, - true); // TODO save - } - )); - // Show the progress dialog now or after dialog confirmation - if (database.validatePasswordEncoding(masterPassword)) { - taskThread.start(); - } else { - new PasswordEncodingDialogHelper() - .show(this, (dialog, which) -> taskThread.start()); - } - } - - @Override - public void onAssignKeyDialogNegativeClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { - - } - - @Override - public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) { - if (listNodesFragment != null) - listNodesFragment.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } - - // Not directly get the entry from intent data but from database - // Is refresh from onResume() - } - - @SuppressLint("RestrictedApi") - @Override - public void startActivityForResult(Intent intent, int requestCode, Bundle options) { - /* - * ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in - * another app such as Files or GoogleDrive and then Search for an entry. Here we remove the - * FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task. - */ - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - int flags = intent.getFlags(); - flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK; - intent.setFlags(flags); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - super.startActivityForResult(intent, requestCode, options); - } - } - - private void removeSearchInIntent(Intent intent) { - if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - currentGroupIsASearch = false; - intent.setAction(Intent.ACTION_DEFAULT); - intent.removeExtra(SearchManager.QUERY); - } - } - - @Override - public void onBackPressed() { - - // Normal way when we are not in root - if (rootGroup!= null && !rootGroup.equals(mCurrentGroup)) - super.onBackPressed(); - // Else lock if needed - else { - if (PreferencesUtil.isLockDatabaseWhenBackButtonOnRootClicked(this)) { - App.Companion.getCurrentDatabase().closeAndClear(getApplicationContext()); - super.onBackPressed(); - } else { - moveTaskToBack(true); - } - } - - listNodesFragment = (ListNodesFragment) getSupportFragmentManager().findFragmentByTag(LIST_NODES_FRAGMENT_TAG); - // to refresh fragment - listNodesFragment.rebuildList(); - mCurrentGroup = listNodesFragment.getMainGroup(); - removeSearchInIntent(getIntent()); - assignGroupViewElements(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt new file mode 100644 index 000000000..55bc7e07d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -0,0 +1,1051 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + */ +package com.kunzisoft.keepass.activities + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.SearchManager +import android.app.assist.AssistStructure +import android.content.ComponentName +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.graphics.Color +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.preference.PreferenceManager +import android.support.annotation.RequiresApi +import android.support.v4.app.FragmentManager +import android.support.v7.widget.SearchView +import android.support.v7.widget.Toolbar +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.ImageView +import android.widget.TextView + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.lock.LockingActivity +import com.kunzisoft.keepass.adapters.NodeAdapter +import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.autofill.AutofillHelper +import com.kunzisoft.keepass.database.SortNodeEnum +import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable +import com.kunzisoft.keepass.database.action.ProgressDialogRunnable +import com.kunzisoft.keepass.database.action.node.ActionNodeValues +import com.kunzisoft.keepass.database.action.node.AddGroupRunnable +import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable +import com.kunzisoft.keepass.database.action.node.CopyEntryRunnable +import com.kunzisoft.keepass.database.action.node.DeleteEntryRunnable +import com.kunzisoft.keepass.database.action.node.DeleteGroupRunnable +import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable +import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable +import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable +import com.kunzisoft.keepass.database.element.* +import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment +import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment +import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment +import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper +import com.kunzisoft.keepass.dialogs.ReadOnlyDialog +import com.kunzisoft.keepass.dialogs.SortDialogFragment +import com.kunzisoft.keepass.education.GroupActivityEducation +import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService +import com.kunzisoft.keepass.magikeyboard.KeyboardHelper +import com.kunzisoft.keepass.magikeyboard.MagikIME +import com.kunzisoft.keepass.model.Entry +import com.kunzisoft.keepass.model.Field +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.timeout.TimeoutHelper +import com.kunzisoft.keepass.utils.MenuUtil +import com.kunzisoft.keepass.view.AddNodeButtonView + +import net.cachapa.expandablelayout.ExpandableLayout + +class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener, NodeAdapter.NodeMenuListener, ListNodesFragment.OnScrollListener, AssignMasterKeyDialogFragment.AssignPasswordDialogListener, NodeAdapter.NodeClickCallback, SortDialogFragment.SortSelectionListener { + + // Views + private var toolbar: Toolbar? = null + private var searchTitleView: View? = null + private var toolbarPasteExpandableLayout: ExpandableLayout? = null + private var toolbarPaste: Toolbar? = null + private var iconView: ImageView? = null + private var modeTitleView: TextView? = null + private var addNodeButtonView: AddNodeButtonView? = null + private var groupNameView: TextView? = null + + private var mDatabase: Database? = null + + private var listNodesFragment: ListNodesFragment? = null + private var currentGroupIsASearch: Boolean = false + + // Nodes + private var mRootGroup: GroupVersioned? = null + private var mCurrentGroup: GroupVersioned? = null + private var mOldGroupToUpdate: GroupVersioned? = null + private var nodeToCopy: NodeVersioned? = null + private var nodeToMove: NodeVersioned? = null + + private var searchSuggestionAdapter: SearchEntryCursorAdapter? = null + + private var iconColor: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (isFinishing) { + return + } + mDatabase = App.currentDatabase + + // Construct main view + setContentView(layoutInflater.inflate(R.layout.list_nodes_with_add_button, null)) + + // Initialize views + iconView = findViewById(R.id.icon) + addNodeButtonView = findViewById(R.id.add_node_button) + toolbar = findViewById(R.id.toolbar) + searchTitleView = findViewById(R.id.search_title) + groupNameView = findViewById(R.id.group_name) + toolbarPasteExpandableLayout = findViewById(R.id.expandable_toolbar_paste_layout) + toolbarPaste = findViewById(R.id.toolbar_paste) + modeTitleView = findViewById(R.id.mode_title_view) + + // Focus view to reinitialize timeout + resetAppTimeoutWhenViewFocusedOrChanged(addNodeButtonView) + + // Retrieve elements after an orientation change + if (savedInstanceState != null) { + if (savedInstanceState.containsKey(OLD_GROUP_TO_UPDATE_KEY)) + mOldGroupToUpdate = savedInstanceState.getParcelable(OLD_GROUP_TO_UPDATE_KEY) + if (savedInstanceState.containsKey(NODE_TO_COPY_KEY)) { + nodeToCopy = savedInstanceState.getParcelable(NODE_TO_COPY_KEY) + toolbarPaste?.setOnMenuItemClickListener(OnCopyMenuItemClickListener()) + } else if (savedInstanceState.containsKey(NODE_TO_MOVE_KEY)) { + nodeToMove = savedInstanceState.getParcelable(NODE_TO_MOVE_KEY) + toolbarPaste?.setOnMenuItemClickListener(OnMoveMenuItemClickListener()) + } + } + + try { + mRootGroup = mDatabase?.rootGroup + } catch (e: NullPointerException) { + Log.e(TAG, "Unable to get rootGroup") + } + + mCurrentGroup = retrieveCurrentGroup(intent, savedInstanceState) + currentGroupIsASearch = Intent.ACTION_SEARCH == intent.action + + Log.i(TAG, "Started creating tree") + if (mCurrentGroup == null) { + Log.w(TAG, "Group was null") + return + } + + // Update last access time. + mCurrentGroup?.touch(modified = false, touchParents = false) + + toolbar?.title = "" + setSupportActionBar(toolbar) + + toolbarPaste?.inflateMenu(R.menu.node_paste_menu) + toolbarPaste?.setNavigationIcon(R.drawable.ic_arrow_left_white_24dp) + toolbarPaste?.setNavigationOnClickListener { + toolbarPasteExpandableLayout?.collapse() + nodeToCopy = null + nodeToMove = null + } + + // Retrieve the textColor to tint the icon + iconColor = theme + .obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) + .getColor(0, Color.WHITE) + + var fragmentTag = LIST_NODES_FRAGMENT_TAG + if (currentGroupIsASearch) + fragmentTag = SEARCH_FRAGMENT_TAG + + // Initialize the fragment with the list + listNodesFragment = supportFragmentManager.findFragmentByTag(fragmentTag) as ListNodesFragment? + if (listNodesFragment == null) + listNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly, currentGroupIsASearch) + + // Attach fragment to content view + supportFragmentManager.beginTransaction().replace( + R.id.nodes_list_fragment_container, + listNodesFragment, + fragmentTag) + .commit() + + // Add listeners to the add buttons + addNodeButtonView?.setAddGroupClickListener { + GroupEditDialogFragment.build() + .show(supportFragmentManager, + GroupEditDialogFragment.TAG_CREATE_GROUP) + } + mCurrentGroup?.let { currentGroup -> + addNodeButtonView?.setAddEntryClickListener { + EntryEditActivity.launch(this@GroupActivity, currentGroup) + } + } + + // Search suggestion + searchSuggestionAdapter = SearchEntryCursorAdapter(this, mDatabase) + + Log.i(TAG, "Finished creating tree") + } + + override fun onNewIntent(intent: Intent) { + Log.d(TAG, "setNewIntent: $intent") + setIntent(intent) + currentGroupIsASearch = if (Intent.ACTION_SEARCH == intent.action) { + // only one instance of search in backstack + openSearchGroup(retrieveCurrentGroup(intent, null)) + true + } else { + false + } + } + + private fun openSearchGroup(group: GroupVersioned?) { + // Delete the previous search fragment + val searchFragment = supportFragmentManager.findFragmentByTag(SEARCH_FRAGMENT_TAG) + if (searchFragment != null) { + if (supportFragmentManager + .popBackStackImmediate(SEARCH_FRAGMENT_TAG, + FragmentManager.POP_BACK_STACK_INCLUSIVE)) + supportFragmentManager.beginTransaction().remove(searchFragment).commit() + } + openGroup(group, true) + } + + private fun openChildGroup(group: GroupVersioned) { + openGroup(group, false) + } + + private fun openGroup(group: GroupVersioned?, isASearch: Boolean) { + // Check TimeoutHelper + TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) { + // Open a group in a new fragment + val newListNodeFragment = ListNodesFragment.newInstance(group, readOnly, isASearch) + val fragmentTransaction = supportFragmentManager.beginTransaction() + // Different animation + val fragmentTag: String + fragmentTag = if (isASearch) { + fragmentTransaction.setCustomAnimations(R.anim.slide_in_top, R.anim.slide_out_bottom, + R.anim.slide_in_bottom, R.anim.slide_out_top) + SEARCH_FRAGMENT_TAG + } else { + fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, + R.anim.slide_in_left, R.anim.slide_out_right) + LIST_NODES_FRAGMENT_TAG + } + + fragmentTransaction.replace(R.id.nodes_list_fragment_container, + newListNodeFragment, + fragmentTag) + fragmentTransaction.addToBackStack(fragmentTag) + fragmentTransaction.commit() + + listNodesFragment = newListNodeFragment + mCurrentGroup = group + assignGroupViewElements() + } + } + + override fun onSaveInstanceState(outState: Bundle) { + mCurrentGroup?.let { + outState.putParcelable(GROUP_ID_KEY, it.nodeId) + } + mOldGroupToUpdate?.let { + outState.putParcelable(OLD_GROUP_TO_UPDATE_KEY, it) + } + nodeToCopy?.let { + outState.putParcelable(NODE_TO_COPY_KEY, it) + } + nodeToMove?.let { + outState.putParcelable(NODE_TO_MOVE_KEY, it) + } + super.onSaveInstanceState(outState) + } + + private fun retrieveCurrentGroup(intent: Intent, savedInstanceState: Bundle?): GroupVersioned? { + + // If it's a search + if (Intent.ACTION_SEARCH == intent.action) { + return mDatabase?.search(intent.getStringExtra(SearchManager.QUERY).trim { it <= ' ' }) + } + // else a real group + else { + var pwGroupId: PwNodeId<*>? = null + if (savedInstanceState != null && savedInstanceState.containsKey(GROUP_ID_KEY)) { + pwGroupId = savedInstanceState.getParcelable(GROUP_ID_KEY) + } else { + if (getIntent() != null) + pwGroupId = intent.getParcelableExtra(GROUP_ID_KEY) + } + + readOnly = mDatabase?.isReadOnly == true || readOnly // Force read only if the database is like that + + Log.w(TAG, "Creating tree view") + val currentGroup: GroupVersioned? + currentGroup = if (pwGroupId == null) { + mRootGroup + } else { + mDatabase?.getGroupById(pwGroupId) + } + + return currentGroup + } + } + + private fun assignGroupViewElements() { + // Assign title + if (mCurrentGroup != null) { + val title = mCurrentGroup?.title + if (title != null && title.isNotEmpty()) { + if (groupNameView != null) { + groupNameView?.text = title + groupNameView?.invalidate() + } + } else { + if (groupNameView != null) { + groupNameView?.text = getText(R.string.root) + groupNameView?.invalidate() + } + } + } + if (currentGroupIsASearch) { + searchTitleView?.visibility = View.VISIBLE + } else { + searchTitleView?.visibility = View.GONE + } + + // Assign icon + if (currentGroupIsASearch) { + if (toolbar != null) { + toolbar?.navigationIcon = null + } + iconView?.visibility = View.GONE + } else { + // Assign the group icon depending of IconPack or custom icon + iconView?.visibility = View.VISIBLE + mCurrentGroup?.let { + mDatabase?.drawFactory?.assignDatabaseIconTo(this, iconView, it.icon, iconColor) + + if (toolbar != null) { + if (mCurrentGroup?.containsParent() == true) + toolbar?.setNavigationIcon(R.drawable.ic_arrow_up_white_24dp) + else { + toolbar?.navigationIcon = null + } + } + } + } + + // Show selection mode message if needed + if (selectionMode) { + modeTitleView?.visibility = View.VISIBLE + } else { + modeTitleView?.visibility = View.GONE + } + + // Show button if allowed + addNodeButtonView?.apply { + + // To enable add button + val addGroupEnabled = !readOnly && !currentGroupIsASearch + var addEntryEnabled = !readOnly && !currentGroupIsASearch + mCurrentGroup?.let { + val isRoot = it == mRootGroup + if (!it.allowAddEntryIfIsRoot()) + addEntryEnabled = !isRoot && addEntryEnabled + if (isRoot) { + showWarnings() + } + } + enableAddGroup(addGroupEnabled) + enableAddEntry(addEntryEnabled) + + if (isEnable) + showButton() + } + } + + override fun onScrolled(dy: Int) { + addNodeButtonView?.hideButtonOnScrollListener(dy) + } + + override fun onNodeClick(node: NodeVersioned) { + when (node.type) { + Type.GROUP -> try { + openChildGroup(node as GroupVersioned) + } catch (e: ClassCastException) { + Log.e(TAG, "Node can't be cast in Group") + } + + Type.ENTRY -> try { + val entry = node as EntryVersioned + EntrySelectionHelper.doEntrySelectionAction(intent, + { + EntryActivity.launch(this@GroupActivity, entry, readOnly) + }, + { + MagikIME.setEntryKey(getEntry(entry)) + // Show the notification if allowed in Preferences + if (PreferencesUtil.enableKeyboardNotificationEntry(this@GroupActivity)) { + startService(Intent( + this@GroupActivity, + KeyboardEntryNotificationService::class.java)) + } + // Consume the selection mode + EntrySelectionHelper.removeEntrySelectionModeFromIntent(intent) + moveTaskToBack(true) + }, + { + // Build response with the entry selected + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.buildResponseWhenEntrySelected(this@GroupActivity, entry) + } + finish() + }) + } catch (e: ClassCastException) { + Log.e(TAG, "Node can't be cast in Entry") + } + } + } + + // TODO Builder + private fun getEntry(entry: EntryVersioned): Entry { + val entryModel = Entry() + entryModel.title = entry.title + entryModel.username = entry.username + entryModel.password = entry.password + entryModel.url = entry.url + if (entry.containsCustomFields()) { + entry.fields + .doActionToAllCustomProtectedField { key, value -> + entryModel.addCustomField( + Field(key, value.toString())) + } + } + return entryModel + } + + override fun onOpenMenuClick(node: NodeVersioned): Boolean { + onNodeClick(node) + return true + } + + override fun onEditMenuClick(node: NodeVersioned): Boolean { + when (node.type) { + Type.GROUP -> { + mOldGroupToUpdate = node as GroupVersioned + GroupEditDialogFragment.build(mOldGroupToUpdate!!) + .show(supportFragmentManager, + GroupEditDialogFragment.TAG_CREATE_GROUP) + } + Type.ENTRY -> EntryEditActivity.launch(this@GroupActivity, node as EntryVersioned) + } + return true + } + + override fun onCopyMenuClick(node: NodeVersioned): Boolean { + toolbarPasteExpandableLayout?.expand() + nodeToCopy = node + toolbarPaste?.setOnMenuItemClickListener(OnCopyMenuItemClickListener()) + return false + } + + private inner class OnCopyMenuItemClickListener : Toolbar.OnMenuItemClickListener { + override fun onMenuItemClick(item: MenuItem): Boolean { + toolbarPasteExpandableLayout?.collapse() + + when (item.itemId) { + R.id.menu_paste -> { + when (nodeToCopy?.type) { + Type.GROUP -> Log.e(TAG, "Copy not allowed for group") + Type.ENTRY -> { + mCurrentGroup?.let { currentGroup -> + copyEntry(nodeToCopy as EntryVersioned, currentGroup) + } + } + } + nodeToCopy = null + return true + } + } + return true + } + } + + private fun copyEntry(entryToCopy: EntryVersioned, newParent: GroupVersioned) { + Thread(CopyEntryRunnable(this, + App.currentDatabase, + entryToCopy, + newParent, + AfterAddNodeRunnable(), + !readOnly) + ).start() + } + + override fun onMoveMenuClick(node: NodeVersioned): Boolean { + toolbarPasteExpandableLayout?.expand() + nodeToMove = node + toolbarPaste?.setOnMenuItemClickListener(OnMoveMenuItemClickListener()) + return false + } + + private inner class OnMoveMenuItemClickListener : Toolbar.OnMenuItemClickListener { + override fun onMenuItemClick(item: MenuItem): Boolean { + toolbarPasteExpandableLayout?.collapse() + + when (item.itemId) { + R.id.menu_paste -> { + when (nodeToMove?.type) { + Type.GROUP -> { + mCurrentGroup?.let { currentGroup -> + moveGroup(nodeToMove as GroupVersioned, currentGroup) + } + } + Type.ENTRY -> { + mCurrentGroup?.let { currentGroup -> + moveEntry(nodeToMove as EntryVersioned, currentGroup) + } + } + } + nodeToMove = null + return true + } + } + return true + } + } + + private fun moveGroup(groupToMove: GroupVersioned, newParent: GroupVersioned) { + Thread(MoveGroupRunnable( + this, + App.currentDatabase, + groupToMove, + newParent, + AfterAddNodeRunnable(), + !readOnly) + ).start() + } + + private fun moveEntry(entryToMove: EntryVersioned, newParent: GroupVersioned) { + Thread(MoveEntryRunnable( + this, + App.currentDatabase, + entryToMove, + newParent, + AfterAddNodeRunnable(), + !readOnly) + ).start() + } + + override fun onDeleteMenuClick(node: NodeVersioned): Boolean { + when (node.type) { + Type.GROUP -> deleteGroup(node as GroupVersioned) + Type.ENTRY -> deleteEntry(node as EntryVersioned) + } + return true + } + + private fun deleteGroup(group: GroupVersioned) { + //TODO Verify trash recycle bin + Thread(DeleteGroupRunnable( + this, + App.currentDatabase, + group, + AfterDeleteNodeRunnable(), + !readOnly) + ).start() + } + + private fun deleteEntry(entry: EntryVersioned) { + Thread(DeleteEntryRunnable( + this, + App.currentDatabase, + entry, + AfterDeleteNodeRunnable(), + !readOnly) + ).start() + } + + override fun onResume() { + super.onResume() + // Refresh the elements + assignGroupViewElements() + // Refresh suggestions to change preferences + searchSuggestionAdapter?.reInit(this) + } + + override fun onStop() { + super.onStop() + // Hide button + addNodeButtonView?.hideButton() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + + val inflater = menuInflater + inflater.inflate(R.menu.search, menu) + inflater.inflate(R.menu.database_lock, menu) + if (!readOnly) + inflater.inflate(R.menu.database_master_key, menu) + if (!selectionMode) { + inflater.inflate(R.menu.default_menu, menu) + MenuUtil.contributionMenuInflater(inflater, menu) + } + + // Get the SearchView and set the searchable configuration + val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager + + menu.findItem(R.id.menu_search)?.let { + val searchView = it.actionView as SearchView? + searchView?.apply { + setSearchableInfo(searchManager.getSearchableInfo( + ComponentName(this@GroupActivity, GroupActivity::class.java))) + setIconifiedByDefault(false) // Do not iconify the widget; expand it by default + suggestionsAdapter = searchSuggestionAdapter + setOnSuggestionListener(object : SearchView.OnSuggestionListener { + override fun onSuggestionClick(position: Int): Boolean { + searchSuggestionAdapter?.let { searchAdapter -> + onNodeClick(searchAdapter.getEntryFromPosition(position)) + } + return true + } + + override fun onSuggestionSelect(position: Int): Boolean { + return true + } + }) + } + } + + super.onCreateOptionsMenu(menu) + + // Launch education screen + Handler().post { performedNextEducation(GroupActivityEducation(this), menu) } + + return true + } + + private fun performedNextEducation(groupActivityEducation: GroupActivityEducation, + menu: Menu) { + // If no node, show education to add new one + if (listNodesFragment != null + && listNodesFragment!!.isEmpty + && addNodeButtonView != null + && addNodeButtonView!!.isEnable + && groupActivityEducation.checkAndPerformedAddNodeButtonEducation( + addNodeButtonView!!, + { + addNodeButtonView?.openButtonIfClose() + }, + { + performedNextEducation(groupActivityEducation, menu) + } + )) + else if (toolbar != null + && toolbar!!.findViewById(R.id.menu_search) != null + && groupActivityEducation.checkAndPerformedSearchMenuEducation( + toolbar!!.findViewById(R.id.menu_search), + { + menu.findItem(R.id.menu_search).expandActionView() + }, + { + performedNextEducation(groupActivityEducation, menu) + })) + else if (toolbar != null + && toolbar!!.findViewById(R.id.menu_sort) != null + && groupActivityEducation.checkAndPerformedSortMenuEducation( + toolbar!!.findViewById(R.id.menu_sort), + { + onOptionsItemSelected(menu.findItem(R.id.menu_sort)) + }, + { + performedNextEducation(groupActivityEducation, menu) + })) + else if (toolbar != null + && toolbar!!.findViewById(R.id.menu_lock) != null + && groupActivityEducation.checkAndPerformedLockMenuEducation( + toolbar!!.findViewById(R.id.menu_lock), + { + onOptionsItemSelected(menu.findItem(R.id.menu_lock)) + }, + { + performedNextEducation(groupActivityEducation, menu) + })) + ; + } + + override fun startActivity(intent: Intent) { + + // Get the intent, verify the action and get the query + if (Intent.ACTION_SEARCH == intent.action) { + val query = intent.getStringExtra(SearchManager.QUERY) + // manually launch the real search activity + val searchIntent = Intent(applicationContext, GroupActivity::class.java) + // add query to the Intent Extras + searchIntent.action = Intent.ACTION_SEARCH + searchIntent.putExtra(SearchManager.QUERY, query) + + EntrySelectionHelper.doEntrySelectionAction(intent, + { + super@GroupActivity.startActivity(intent) + }, + { + KeyboardHelper.startActivityForKeyboardSelection( + this@GroupActivity, + searchIntent) + finish() + }, + { assistStructure -> + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.startActivityForAutofillResult( + this@GroupActivity, + searchIntent, + assistStructure) + } + }) + } else { + super.startActivity(intent) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + onBackPressed() + return true + } + R.id.menu_search -> + //onSearchRequested(); + return true + R.id.menu_lock -> { + lockAndExit() + return true + } + R.id.menu_change_master_key -> { + setPassword() + return true + } + else -> { + // Check the time lock before launching settings + MenuUtil.onDefaultMenuOptionsItemSelected(this, item, readOnly, true) + return super.onOptionsItemSelected(item) + } + } + } + + private fun setPassword() { + AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog") + } + + override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction, + name: String, + icon: PwIcon) { + val database = App.currentDatabase + + when (action) { + GroupEditDialogFragment.EditGroupDialogAction.CREATION -> { + // If group creation + mCurrentGroup?.let { currentGroup -> + // Build the group + database.createGroup()?.let { newGroup-> + newGroup.title = name + newGroup.icon = icon + // Not really needed here because added in runnable but safe + newGroup.parent = currentGroup + + // If group created save it in the database + Thread(AddGroupRunnable(this, + App.currentDatabase, + newGroup, + currentGroup, + AfterAddNodeRunnable(), + !readOnly) + ).start() + } + } + } + GroupEditDialogFragment.EditGroupDialogAction.UPDATE -> + // If update add new elements + mOldGroupToUpdate?.let { oldGroupToUpdate -> + GroupVersioned(oldGroupToUpdate).let { updateGroup -> + updateGroup.title = name + // TODO custom icon + updateGroup.icon = icon + + listNodesFragment?.removeNode(oldGroupToUpdate) + + // If group updated save it in the database + Thread(UpdateGroupRunnable(this, + App.currentDatabase, + oldGroupToUpdate, + updateGroup, + AfterUpdateNodeRunnable(), + !readOnly) + ).start() + } + } + else -> {} + } + } + + internal inner class AfterAddNodeRunnable : AfterActionNodeFinishRunnable() { + override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { + runOnUiThread { + if (actionNodeValues.success) { + listNodesFragment?.addNode(actionNodeValues.newNode) + } + } + } + } + + internal inner class AfterUpdateNodeRunnable : AfterActionNodeFinishRunnable() { + override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { + runOnUiThread { + if (actionNodeValues.success) { + listNodesFragment?.updateNode(actionNodeValues.oldNode, actionNodeValues.newNode) + } + } + } + } + + internal inner class AfterDeleteNodeRunnable : AfterActionNodeFinishRunnable() { + override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { + runOnUiThread { + if (actionNodeValues.success) { + listNodesFragment?.removeNode(actionNodeValues.oldNode) + + actionNodeValues.oldNode?.let { oldNode -> + val parent = oldNode.parent + val database = App.currentDatabase + if (database.isRecycleBinAvailable && database.isRecycleBinEnabled) { + val recycleBin = database.recycleBin + // Add trash if it doesn't exists + if (parent == recycleBin + && mCurrentGroup != null + && mCurrentGroup!!.parent == null + && mCurrentGroup != recycleBin) { + listNodesFragment?.addNode(parent) + } + } + } + } + } + } + } + + override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction, + name: String, + iconId: PwIcon) { + // Do nothing here + } + + override// For icon in create tree dialog + fun iconPicked(bundle: Bundle) { + (supportFragmentManager + .findFragmentByTag(GroupEditDialogFragment.TAG_CREATE_GROUP) as GroupEditDialogFragment) + .iconPicked(bundle) + } + + private fun showWarnings() { + // TODO Preferences + if (App.currentDatabase.isReadOnly) { + val prefs = PreferenceManager.getDefaultSharedPreferences(this) + if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) { + ReadOnlyDialog(this).show() + } + } + } + + override fun onAssignKeyDialogPositiveClick( + masterPasswordChecked: Boolean, masterPassword: String?, + keyFileChecked: Boolean, keyFile: Uri?) { + + mDatabase?.let { database -> + val taskThread = Thread(ProgressDialogRunnable(this, + R.string.saving_database + ) { + AssignPasswordInDatabaseRunnable(this@GroupActivity, + database, + masterPasswordChecked, + masterPassword, + keyFileChecked, + keyFile, + true) // TODO save + }) + // Show the progress dialog now or after dialog confirmation + if (database.validatePasswordEncoding(masterPassword!!)) { + taskThread.start() + } else { + PasswordEncodingDialogHelper() + .show(this, DialogInterface.OnClickListener{ _, _ -> taskThread.start() }) + } + } + } + + override fun onAssignKeyDialogNegativeClick( + masterPasswordChecked: Boolean, masterPassword: String?, + keyFileChecked: Boolean, keyFile: Uri?) { + + } + + override fun onSortSelected(sortNodeEnum: SortNodeEnum, ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) { + listNodesFragment?.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) + } + + // Not directly get the entry from intent data but from database + // Is refresh from onResume() + } + + @SuppressLint("RestrictedApi") + override fun startActivityForResult(intent: Intent, requestCode: Int, options: Bundle?) { + /* + * ACTION_SEARCH automatically forces a new task. This occurs when you open a kdb file in + * another app such as Files or GoogleDrive and then Search for an entry. Here we remove the + * FLAG_ACTIVITY_NEW_TASK flag bit allowing search to open it's activity in the current task. + */ + if (Intent.ACTION_SEARCH == intent.action) { + var flags = intent.flags + flags = flags and Intent.FLAG_ACTIVITY_NEW_TASK.inv() + intent.flags = flags + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + super.startActivityForResult(intent, requestCode, options) + } + } + + private fun removeSearchInIntent(intent: Intent) { + if (Intent.ACTION_SEARCH == intent.action) { + currentGroupIsASearch = false + intent.action = Intent.ACTION_DEFAULT + intent.removeExtra(SearchManager.QUERY) + } + } + + override fun onBackPressed() { + + // Normal way when we are not in root + if (mRootGroup != null && mRootGroup != mCurrentGroup) + super.onBackPressed() + // Else lock if needed + else { + if (PreferencesUtil.isLockDatabaseWhenBackButtonOnRootClicked(this)) { + App.currentDatabase.closeAndClear(applicationContext) + super.onBackPressed() + } else { + moveTaskToBack(true) + } + } + + listNodesFragment = supportFragmentManager.findFragmentByTag(LIST_NODES_FRAGMENT_TAG) as ListNodesFragment + // to refresh fragment + listNodesFragment?.rebuildList() + mCurrentGroup = listNodesFragment?.mainGroup + removeSearchInIntent(intent) + assignGroupViewElements() + } + + companion object { + + private val TAG = GroupActivity::class.java.name + + private const val GROUP_ID_KEY = "GROUP_ID_KEY" + private const val LIST_NODES_FRAGMENT_TAG = "LIST_NODES_FRAGMENT_TAG" + private const val SEARCH_FRAGMENT_TAG = "SEARCH_FRAGMENT_TAG" + private const val OLD_GROUP_TO_UPDATE_KEY = "OLD_GROUP_TO_UPDATE_KEY" + private const val NODE_TO_COPY_KEY = "NODE_TO_COPY_KEY" + private const val NODE_TO_MOVE_KEY = "NODE_TO_MOVE_KEY" + + private fun buildAndLaunchIntent(activity: Activity, group: GroupVersioned?, readOnly: Boolean, + intentBuildLauncher: (Intent) -> Unit) { + if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { + val intent = Intent(activity, GroupActivity::class.java) + if (group != null) { + intent.putExtra(GROUP_ID_KEY, group.nodeId) + } + ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly) + intentBuildLauncher.invoke(intent) + } + } + + /* + * ------------------------- + * Standard Launch + * ------------------------- + */ + + @JvmOverloads + fun launch(activity: Activity, readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(activity)) { + launch(activity, null, readOnly) + } + + fun launch(activity: Activity, group: GroupVersioned?, readOnly: Boolean) { + TimeoutHelper.recordTime(activity) + buildAndLaunchIntent(activity, group, readOnly) { intent -> + activity.startActivityForResult(intent, 0) + } + } + + /* + * ------------------------- + * Keyboard Launch + * ------------------------- + */ + // TODO implement pre search to directly open the direct group + + fun launchForKeyboardSelection(activity: Activity, readOnly: Boolean) { + TimeoutHelper.recordTime(activity) + buildAndLaunchIntent(activity, null, readOnly) { intent -> + KeyboardHelper.startActivityForKeyboardSelection(activity, intent) + } + } + + /* + * ------------------------- + * Autofill Launch + * ------------------------- + */ + // TODO implement pre search to directly open the direct group + + @RequiresApi(api = Build.VERSION_CODES.O) + fun launchForAutofillResult(activity: Activity, assistStructure: AssistStructure, readOnly: Boolean) { + TimeoutHelper.recordTime(activity) + buildAndLaunchIntent(activity, null, readOnly) { intent -> + AutofillHelper.startActivityForAutofillResult(activity, intent, assistStructure) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt index 90f0d3326..c15a114d5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt @@ -24,7 +24,6 @@ import android.net.Uri import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.InvalidKeyFileException import com.kunzisoft.keepass.tasks.ActionRunnable -import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.getUriInputStream import java.io.IOException @@ -35,8 +34,8 @@ class AssignPasswordInDatabaseRunnable @JvmOverloads constructor( masterPassword: String?, withKeyFile: Boolean, keyFile: Uri?, - actionRunnable: ActionRunnable? = null, - save: Boolean) + save: Boolean, + actionRunnable: ActionRunnable? = null) : SaveDatabaseRunnable(ctx, db, actionRunnable, save) { private var mMasterPassword: String? = null diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java index 08007f6ef..345a357dd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java @@ -468,8 +468,8 @@ public class FileDatabaseSelectActivity extends StylishActivity implements masterPassword, keyFileChecked, keyFile, - new LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename)), - true // TODO get readonly + true, // TODO get readonly + new LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename)) ); }) )).start(); @@ -505,7 +505,7 @@ public class FileDatabaseSelectActivity extends StylishActivity implements fileDatabaseHistory.addDatabaseUri(fileURI); mAdapter.notifyDataSetChanged(); updateFileListVisibility(); - GroupActivity.launch(FileDatabaseSelectActivity.this); + GroupActivity.Companion.launch(FileDatabaseSelectActivity.this); } else { Log.e(TAG, "Unable to open the database"); } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IntentBuildLauncher.java b/app/src/main/java/com/kunzisoft/keepass/password/IntentBuildLauncher.java similarity index 73% rename from app/src/main/java/com/kunzisoft/keepass/activities/IntentBuildLauncher.java rename to app/src/main/java/com/kunzisoft/keepass/password/IntentBuildLauncher.java index 0d3e0e4be..aa69659d1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IntentBuildLauncher.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/IntentBuildLauncher.java @@ -1,4 +1,4 @@ -package com.kunzisoft.keepass.activities; +package com.kunzisoft.keepass.password; import android.content.Intent; diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java index 685898e4f..5b7a941c3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java @@ -52,7 +52,6 @@ import android.widget.Toast; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.activities.EntrySelectionHelper; import com.kunzisoft.keepass.activities.GroupActivity; -import com.kunzisoft.keepass.activities.IntentBuildLauncher; import com.kunzisoft.keepass.activities.ReadOnlyHelper; import com.kunzisoft.keepass.activities.lock.LockingActivity; import com.kunzisoft.keepass.app.App; @@ -826,18 +825,18 @@ public class PasswordActivity extends StylishActivity private void launchGroupActivity() { EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), () -> { - GroupActivity.launch(PasswordActivity.this, readOnly); + GroupActivity.Companion.launch(PasswordActivity.this, readOnly); return null; }, () -> { - GroupActivity.launchForKeyboardSelection(PasswordActivity.this, readOnly); + GroupActivity.Companion.launchForKeyboardSelection(PasswordActivity.this, readOnly); // Do not keep history finish(); return null; }, assistStructure -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - GroupActivity.launchForAutofillResult(PasswordActivity.this, assistStructure, readOnly); + GroupActivity.Companion.launchForAutofillResult(PasswordActivity.this, assistStructure, readOnly); } return null; }); From cd271e6f04e8e7010f5112377fbcd96c77f407d4 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 20:22:06 +0200 Subject: [PATCH 138/289] Kotlinized ListNodesFragment --- .../keepass/activities/GroupActivity.kt | 30 +- .../keepass/activities/ListNodesFragment.java | 294 ------------------ .../keepass/activities/ListNodesFragment.kt | 277 +++++++++++++++++ 3 files changed, 294 insertions(+), 307 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 55bc7e07d..8f229a27e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -818,7 +818,8 @@ class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListen override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { runOnUiThread { if (actionNodeValues.success) { - listNodesFragment?.addNode(actionNodeValues.newNode) + if (actionNodeValues.newNode != null) + listNodesFragment?.addNode(actionNodeValues.newNode) } } } @@ -828,7 +829,8 @@ class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListen override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { runOnUiThread { if (actionNodeValues.success) { - listNodesFragment?.updateNode(actionNodeValues.oldNode, actionNodeValues.newNode) + if (actionNodeValues.oldNode!= null && actionNodeValues.newNode != null) + listNodesFragment?.updateNode(actionNodeValues.oldNode, actionNodeValues.newNode) } } } @@ -838,19 +840,21 @@ class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListen override fun onActionNodeFinish(actionNodeValues: ActionNodeValues) { runOnUiThread { if (actionNodeValues.success) { - listNodesFragment?.removeNode(actionNodeValues.oldNode) + if (actionNodeValues.oldNode != null) + listNodesFragment?.removeNode(actionNodeValues.oldNode) actionNodeValues.oldNode?.let { oldNode -> - val parent = oldNode.parent - val database = App.currentDatabase - if (database.isRecycleBinAvailable && database.isRecycleBinEnabled) { - val recycleBin = database.recycleBin - // Add trash if it doesn't exists - if (parent == recycleBin - && mCurrentGroup != null - && mCurrentGroup!!.parent == null - && mCurrentGroup != recycleBin) { - listNodesFragment?.addNode(parent) + oldNode.parent?.let { parent -> + val database = App.currentDatabase + if (database.isRecycleBinAvailable && database.isRecycleBinEnabled) { + val recycleBin = database.recycleBin + // Add trash if it doesn't exists + if (parent == recycleBin + && mCurrentGroup != null + && mCurrentGroup!!.parent == null + && mCurrentGroup != recycleBin) { + listNodesFragment?.addNode(parent) + } } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java deleted file mode 100644 index 9d82dcc76..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.java +++ /dev/null @@ -1,294 +0,0 @@ -package com.kunzisoft.keepass.activities; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.adapters.NodeAdapter; -import com.kunzisoft.keepass.database.SortNodeEnum; -import com.kunzisoft.keepass.database.element.GroupVersioned; -import com.kunzisoft.keepass.database.element.NodeVersioned; -import com.kunzisoft.keepass.dialogs.SortDialogFragment; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.stylish.StylishFragment; - -public class ListNodesFragment extends StylishFragment implements - SortDialogFragment.SortSelectionListener { - - private static final String TAG = ListNodesFragment.class.getName(); - - private static final String GROUP_KEY = "GROUP_KEY"; - private static final String IS_SEARCH = "IS_SEARCH"; - - private NodeAdapter.NodeClickCallback nodeClickCallback; - private NodeAdapter.NodeMenuListener nodeMenuListener; - private OnScrollListener onScrollListener; - - private RecyclerView listView; - private GroupVersioned currentGroup; - private NodeAdapter mAdapter; - - private View notFoundView; - private boolean isASearchResult; - - // Preferences for sorting - private SharedPreferences prefs; - - private boolean readOnly; - - public static ListNodesFragment newInstance(GroupVersioned group, boolean readOnly, boolean isASearch) { - Bundle bundle = new Bundle(); - if (group != null) { - bundle.putParcelable(GROUP_KEY, group); - } - bundle.putBoolean(IS_SEARCH, isASearch); - ReadOnlyHelper.INSTANCE.putReadOnlyInBundle(bundle, readOnly); - ListNodesFragment listNodesFragment = new ListNodesFragment(); - listNodesFragment.setArguments(bundle); - return listNodesFragment; - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - try { - nodeClickCallback = (NodeAdapter.NodeClickCallback) context; - } catch (ClassCastException e) { - // The activity doesn't implement the interface, throw exception - throw new ClassCastException(context.toString() - + " must implement " + NodeAdapter.NodeClickCallback.class.getName()); - } - try { - nodeMenuListener = (NodeAdapter.NodeMenuListener) context; - } catch (ClassCastException e) { - nodeMenuListener = null; - // Context menu can be omit - Log.w(TAG, context.toString() - + " must implement " + NodeAdapter.NodeMenuListener.class.getName()); - } - try { - onScrollListener = (OnScrollListener) context; - } catch (ClassCastException e) { - onScrollListener = null; - // Context menu can be omit - Log.w(TAG, context.toString() - + " must implement " + RecyclerView.OnScrollListener.class.getName()); - } - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if ( getActivity() != null ) { - setHasOptionsMenu(true); - - readOnly = ReadOnlyHelper.INSTANCE.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, getArguments()); - - if (getArguments() != null) { - // Contains all the group in element - if (getArguments().containsKey(GROUP_KEY)) { - currentGroup = getArguments().getParcelable(GROUP_KEY); - } - - if (getArguments().containsKey(IS_SEARCH)) { - isASearchResult = getArguments().getBoolean(IS_SEARCH); - } - } - - mAdapter = new NodeAdapter(getContextThemed(), getActivity().getMenuInflater()); - mAdapter.setReadOnly(readOnly); - mAdapter.setIsASearchResult(isASearchResult); - mAdapter.setOnNodeClickListener(nodeClickCallback); - - if (nodeMenuListener != null) { - mAdapter.setActivateContextMenu(true); - mAdapter.setNodeMenuListener(nodeMenuListener); - } - prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - } - } - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - ReadOnlyHelper.INSTANCE.onSaveInstanceState(outState, readOnly); - super.onSaveInstanceState(outState); - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - - // To apply theme - View rootView = inflater.cloneInContext(getContextThemed()) - .inflate(R.layout.list_nodes_fragment, container, false); - listView = rootView.findViewById(R.id.nodes_list); - notFoundView = rootView.findViewById(R.id.not_found_container); - - if (onScrollListener != null) { - listView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - super.onScrolled(recyclerView, dx, dy); - onScrollListener.onScrolled(dy); - } - }); - } - - return rootView; - } - - @Override - public void onResume() { - super.onResume(); - - rebuildList(); - - if (isASearchResult && mAdapter.isEmpty()) { - // To show the " no search entry found " - listView.setVisibility(View.GONE); - notFoundView.setVisibility(View.VISIBLE); - } else { - listView.setVisibility(View.VISIBLE); - notFoundView.setVisibility(View.GONE); - } - } - - public void rebuildList() { - // Add elements to the list - if (currentGroup != null) - mAdapter.rebuildList(currentGroup); - assignListToNodeAdapter(listView); - } - - protected void assignListToNodeAdapter(RecyclerView recyclerView) { - recyclerView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - recyclerView.setAdapter(mAdapter); - } - - @Override - public void onSortSelected(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore, boolean recycleBinBottom) { - // Toggle setting - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(getString(R.string.sort_node_key), sortNodeEnum.name()); - editor.putBoolean(getString(R.string.sort_ascending_key), ascending); - editor.putBoolean(getString(R.string.sort_group_before_key), groupsBefore); - editor.putBoolean(getString(R.string.sort_recycle_bin_bottom_key), recycleBinBottom); - editor.apply(); - - // Tell the adapter to refresh it's list - mAdapter.notifyChangeSort(sortNodeEnum, ascending, groupsBefore); - mAdapter.rebuildList(currentGroup); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.tree, menu); - - super.onCreateOptionsMenu(menu, inflater); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch ( item.getItemId() ) { - - case R.id.menu_sort: - SortDialogFragment sortDialogFragment; - - /* - // TODO Recycle bin bottom - if (database.isRecycleBinAvailable() && database.isRecycleBinEnabled()) { - sortDialogFragment = - SortDialogFragment.getInstance( - PrefsUtil.getListSort(this), - PrefsUtil.getAscendingSort(this), - PrefsUtil.getGroupsBeforeSort(this), - PrefsUtil.getRecycleBinBottomSort(this)); - } else { - */ - sortDialogFragment = - SortDialogFragment.getInstance( - PreferencesUtil.getListSort(getContext()), - PreferencesUtil.getAscendingSort(getContext()), - PreferencesUtil.getGroupsBeforeSort(getContext())); - //} - - sortDialogFragment.show(getChildFragmentManager(), "sortDialog"); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - switch (requestCode) { - case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE: - if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE || - resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { - NodeVersioned newNode = data.getParcelableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY); - if (newNode != null) { - if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE) - mAdapter.addNode(newNode); - if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { - //mAdapter.updateLastNodeRegister(newNode); - mAdapter.rebuildList(currentGroup); - } - } else { - Log.e(this.getClass().getName(), "New node can be retrieve in Activity Result"); - } - } - break; - } - } - - public boolean isEmpty() { - return mAdapter == null || mAdapter.getItemCount() <= 0; - } - - public void addNode(NodeVersioned newNode) { - mAdapter.addNode(newNode); - } - - public void updateNode(NodeVersioned oldNode, NodeVersioned newNode) { - mAdapter.updateNode(oldNode, newNode); - } - - public void removeNode(NodeVersioned pwNode) { - mAdapter.removeNode(pwNode); - } - - public GroupVersioned getMainGroup() { - return currentGroup; - } - - public interface OnScrollListener { - - /** - * Callback method to be invoked when the RecyclerView has been scrolled. This will be - * called after the scroll has completed. - * - * @param dy The amount of vertical scroll. - */ - void onScrolled(int dy); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt new file mode 100644 index 000000000..465c11c04 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt @@ -0,0 +1,277 @@ +package com.kunzisoft.keepass.activities + +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.os.Bundle +import android.preference.PreferenceManager +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.util.Log +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.adapters.NodeAdapter +import com.kunzisoft.keepass.database.SortNodeEnum +import com.kunzisoft.keepass.database.element.GroupVersioned +import com.kunzisoft.keepass.database.element.NodeVersioned +import com.kunzisoft.keepass.dialogs.SortDialogFragment +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.stylish.StylishFragment + +class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionListener { + + private var nodeClickCallback: NodeAdapter.NodeClickCallback? = null + private var nodeMenuListener: NodeAdapter.NodeMenuListener? = null + private var onScrollListener: OnScrollListener? = null + + private var listView: RecyclerView? = null + var mainGroup: GroupVersioned? = null + private set + private var mAdapter: NodeAdapter? = null + + private var notFoundView: View? = null + private var isASearchResult: Boolean = false + + // Preferences for sorting + private var prefs: SharedPreferences? = null + + private var readOnly: Boolean = false + + val isEmpty: Boolean + get() = mAdapter == null || mAdapter?.itemCount?:0 <= 0 + + override fun onAttach(context: Context?) { + super.onAttach(context) + try { + nodeClickCallback = context as NodeAdapter.NodeClickCallback? + } catch (e: ClassCastException) { + // The activity doesn't implement the interface, throw exception + throw ClassCastException(context?.toString() + + " must implement " + NodeAdapter.NodeClickCallback::class.java.name) + } + + try { + nodeMenuListener = context as NodeAdapter.NodeMenuListener? + } catch (e: ClassCastException) { + nodeMenuListener = null + // Context menu can be omit + Log.w(TAG, context?.toString() + + " must implement " + NodeAdapter.NodeMenuListener::class.java.name) + } + + try { + onScrollListener = context as OnScrollListener? + } catch (e: ClassCastException) { + onScrollListener = null + // Context menu can be omit + Log.w(TAG, context?.toString() + + " must implement " + RecyclerView.OnScrollListener::class.java.name) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.let { currentActivity -> + setHasOptionsMenu(true) + + readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, arguments) + + arguments?.let { args -> + // Contains all the group in element + if (args.containsKey(GROUP_KEY)) { + mainGroup = args.getParcelable(GROUP_KEY) + } + if (args.containsKey(IS_SEARCH)) { + isASearchResult = args.getBoolean(IS_SEARCH) + } + } + + mAdapter = NodeAdapter(getContextThemed(), currentActivity.menuInflater) + mAdapter?.apply { + setReadOnly(readOnly) + setIsASearchResult(isASearchResult) + setOnNodeClickListener(nodeClickCallback) + nodeMenuListener?.let { menuListener -> + setActivateContextMenu(true) + setNodeMenuListener(menuListener) + } + } + prefs = PreferenceManager.getDefaultSharedPreferences(context) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + ReadOnlyHelper.onSaveInstanceState(outState, readOnly) + super.onSaveInstanceState(outState) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + super.onCreateView(inflater, container, savedInstanceState) + + // To apply theme + val rootView = inflater.cloneInContext(getContextThemed()) + .inflate(R.layout.list_nodes_fragment, container, false) + listView = rootView.findViewById(R.id.nodes_list) + notFoundView = rootView.findViewById(R.id.not_found_container) + + onScrollListener?.let { onScrollListener -> + listView?.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + onScrollListener.onScrolled(dy) + } + }) + } + + return rootView + } + + override fun onResume() { + super.onResume() + + rebuildList() + + if (isASearchResult && mAdapter!= null && mAdapter!!.isEmpty) { + // To show the " no search entry found " + listView?.visibility = View.GONE + notFoundView?.visibility = View.VISIBLE + } else { + listView?.visibility = View.VISIBLE + notFoundView?.visibility = View.GONE + } + } + + fun rebuildList() { + // Add elements to the list + mainGroup?.let { + mAdapter?.rebuildList(mainGroup) + } + listView?.apply { + scrollBarStyle = View.SCROLLBARS_INSIDE_INSET + layoutManager = LinearLayoutManager(context) + adapter = mAdapter + } + } + + override fun onSortSelected(sortNodeEnum: SortNodeEnum, ascending: Boolean, groupsBefore: Boolean, recycleBinBottom: Boolean) { + // Toggle setting + prefs?.edit()?.apply { + putString(getString(R.string.sort_node_key), sortNodeEnum.name) + putBoolean(getString(R.string.sort_ascending_key), ascending) + putBoolean(getString(R.string.sort_group_before_key), groupsBefore) + putBoolean(getString(R.string.sort_recycle_bin_bottom_key), recycleBinBottom) + apply() + } + + // Tell the adapter to refresh it's list + mAdapter?.notifyChangeSort(sortNodeEnum, ascending, groupsBefore) + mAdapter?.rebuildList(mainGroup) + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + inflater?.inflate(R.menu.tree, menu) + + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + + R.id.menu_sort -> { + val sortDialogFragment: SortDialogFragment + + /* + // TODO Recycle bin bottom + if (database.isRecycleBinAvailable() && database.isRecycleBinEnabled()) { + sortDialogFragment = + 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)) + //} + + sortDialogFragment.show(childFragmentManager, "sortDialog") + return true + } + + else -> return super.onOptionsItemSelected(item) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + when (requestCode) { + EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE -> { + if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE + || resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { + data?.getParcelableExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY)?.let { newNode -> + if (resultCode == EntryEditActivity.ADD_ENTRY_RESULT_CODE) + mAdapter?.addNode(newNode) + if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { + //mAdapter.updateLastNodeRegister(newNode); + mAdapter?.rebuildList(mainGroup) + } + } ?: Log.e(this.javaClass.name, "New node can be retrieve in Activity Result") + } + } + } + } + + fun addNode(newNode: NodeVersioned) { + mAdapter?.addNode(newNode) + } + + fun updateNode(oldNode: NodeVersioned, newNode: NodeVersioned) { + mAdapter?.updateNode(oldNode, newNode) + } + + fun removeNode(pwNode: NodeVersioned) { + mAdapter?.removeNode(pwNode) + } + + interface OnScrollListener { + + /** + * Callback method to be invoked when the RecyclerView has been scrolled. This will be + * called after the scroll has completed. + * + * @param dy The amount of vertical scroll. + */ + fun onScrolled(dy: Int) + } + + companion object { + + private val TAG = ListNodesFragment::class.java.name + + private const val GROUP_KEY = "GROUP_KEY" + private const val IS_SEARCH = "IS_SEARCH" + + fun newInstance(group: GroupVersioned?, readOnly: Boolean, isASearch: Boolean): ListNodesFragment { + val bundle = Bundle() + if (group != null) { + bundle.putParcelable(GROUP_KEY, group) + } + bundle.putBoolean(IS_SEARCH, isASearch) + ReadOnlyHelper.putReadOnlyInBundle(bundle, readOnly) + val listNodesFragment = ListNodesFragment() + listNodesFragment.arguments = bundle + return listNodesFragment + } + } +} From b0bc0f9d856bd417e0f3c63925b0ecfc7ddf66af Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 20:53:08 +0200 Subject: [PATCH 139/289] Kotlinized Adapters and fix StyledAttributes --- .../keepass/activities/EntryActivity.kt | 6 +- .../keepass/activities/EntryEditActivity.kt | 6 +- .../keepass/activities/GroupActivity.kt | 23 +- .../keepass/activities/ListNodesFragment.kt | 16 +- ...asicViewHolder.java => BasicViewHolder.kt} | 26 +- ...ntryViewHolder.java => EntryViewHolder.kt} | 21 +- ...roupViewHolder.java => GroupViewHolder.kt} | 21 +- .../keepass/adapters/NodeAdapter.java | 406 ------------------ .../kunzisoft/keepass/adapters/NodeAdapter.kt | 362 ++++++++++++++++ .../adapters/SearchEntryCursorAdapter.java | 138 ------ .../adapters/SearchEntryCursorAdapter.kt | 124 ++++++ .../dialogs/GroupEditDialogFragment.java | 1 + .../dialogs/IconPickerDialogFragment.java | 1 + .../keepass/view/EntryContentsView.kt | 2 +- 14 files changed, 552 insertions(+), 601 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/adapters/{BasicViewHolder.java => BasicViewHolder.kt} (61%) rename app/src/main/java/com/kunzisoft/keepass/adapters/{EntryViewHolder.java => EntryViewHolder.kt} (59%) rename app/src/main/java/com/kunzisoft/keepass/adapters/{GroupViewHolder.java => GroupViewHolder.kt} (59%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index 04f346e0b..2d582cfe9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -99,9 +99,9 @@ class EntryActivity : LockingHideActivity() { mEntry?.touch(modified = false, touchParents = false) // Retrieve the textColor to tint the icon - iconColor = theme. - obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) - .getColor(0, Color.WHITE) + val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) + iconColor = taIconColor.getColor(0, Color.WHITE) + taIconColor.recycle() // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set invalidateOptionsMenu() diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index 4797af08b..92b042499 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -129,9 +129,9 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi mDatabase = App.currentDatabase // Retrieve the textColor to tint the icon - iconColor = theme - .obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary)) - .getColor(0, Color.WHITE) + val taIconColor = theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary)) + iconColor = taIconColor.getColor(0, Color.WHITE) + taIconColor.recycle() mSelectedIconStandard = mDatabase?.iconFactory?.unknownIcon diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 8f229a27e..117235011 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -81,7 +81,14 @@ import com.kunzisoft.keepass.view.AddNodeButtonView import net.cachapa.expandablelayout.ExpandableLayout -class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListener, IconPickerDialogFragment.IconPickerListener, NodeAdapter.NodeMenuListener, ListNodesFragment.OnScrollListener, AssignMasterKeyDialogFragment.AssignPasswordDialogListener, NodeAdapter.NodeClickCallback, SortDialogFragment.SortSelectionListener { +class GroupActivity : LockingActivity(), + GroupEditDialogFragment.EditGroupListener, + IconPickerDialogFragment.IconPickerListener, + NodeAdapter.NodeMenuListener, + ListNodesFragment.OnScrollListener, + AssignMasterKeyDialogFragment.AssignPasswordDialogListener, + NodeAdapter.NodeClickCallback, + SortDialogFragment.SortSelectionListener { // Views private var toolbar: Toolbar? = null @@ -176,9 +183,9 @@ class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListen } // Retrieve the textColor to tint the icon - iconColor = theme - .obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) - .getColor(0, Color.WHITE) + val taTextColor = theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) + iconColor = taTextColor.getColor(0, Color.WHITE) + taTextColor.recycle() var fragmentTag = LIST_NODES_FRAGMENT_TAG if (currentGroupIsASearch) @@ -209,7 +216,9 @@ class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListen } // Search suggestion - searchSuggestionAdapter = SearchEntryCursorAdapter(this, mDatabase) + mDatabase?.let { database -> + searchSuggestionAdapter = SearchEntryCursorAdapter(this, database) + } Log.i(TAG, "Finished creating tree") } @@ -631,7 +640,9 @@ class GroupActivity : LockingActivity(), GroupEditDialogFragment.EditGroupListen setOnSuggestionListener(object : SearchView.OnSuggestionListener { override fun onSuggestionClick(position: Int): Boolean { searchSuggestionAdapter?.let { searchAdapter -> - onNodeClick(searchAdapter.getEntryFromPosition(position)) + searchAdapter.getEntryFromPosition(position)?.let { entry -> + onNodeClick(entry) + } } return true } 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 465c11c04..23bd0b8a0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt @@ -98,10 +98,8 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis setReadOnly(readOnly) setIsASearchResult(isASearchResult) setOnNodeClickListener(nodeClickCallback) - nodeMenuListener?.let { menuListener -> - setActivateContextMenu(true) - setNodeMenuListener(menuListener) - } + setActivateContextMenu(true) + setNodeMenuListener(nodeMenuListener) } prefs = PreferenceManager.getDefaultSharedPreferences(context) } @@ -150,7 +148,7 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis fun rebuildList() { // Add elements to the list - mainGroup?.let { + mainGroup?.let { mainGroup -> mAdapter?.rebuildList(mainGroup) } listView?.apply { @@ -172,7 +170,9 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis // Tell the adapter to refresh it's list mAdapter?.notifyChangeSort(sortNodeEnum, ascending, groupsBefore) - mAdapter?.rebuildList(mainGroup) + mainGroup?.let { mainGroup -> + mAdapter?.rebuildList(mainGroup) + } } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { @@ -224,7 +224,9 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis mAdapter?.addNode(newNode) if (resultCode == EntryEditActivity.UPDATE_ENTRY_RESULT_CODE) { //mAdapter.updateLastNodeRegister(newNode); - mAdapter?.rebuildList(mainGroup) + mainGroup?.let { mainGroup -> + mAdapter?.rebuildList(mainGroup) + } } } ?: Log.e(this.javaClass.name, "New node can be retrieve in Activity Result") } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.java b/app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.kt similarity index 61% rename from app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.java rename to app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.kt index d84d1e0aa..4d5ab9169 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,21 +17,17 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.adapters; +package com.kunzisoft.keepass.adapters -import android.support.v7.widget.RecyclerView; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; +import android.support.v7.widget.RecyclerView +import android.view.View +import android.widget.ImageView +import android.widget.TextView -abstract class BasicViewHolder extends RecyclerView.ViewHolder { +abstract class BasicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - View container; - ImageView icon; - TextView text; - TextView subText; - - BasicViewHolder(View itemView) { - super(itemView); - } + var container: View? = null + var icon: ImageView? = null + var text: TextView? = null + var subText: TextView? = null } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.java b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.kt similarity index 59% rename from app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.java rename to app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.kt index e24f10497..7b1ffecc1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,19 +17,18 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.adapters; +package com.kunzisoft.keepass.adapters -import android.view.View; +import android.view.View -import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.R -class EntryViewHolder extends BasicViewHolder { +internal class EntryViewHolder(itemView: View) : BasicViewHolder(itemView) { - EntryViewHolder(View itemView) { - super(itemView); - container = itemView.findViewById(R.id.entry_container); - icon = itemView.findViewById(R.id.entry_icon); - text = itemView.findViewById(R.id.entry_text); - subText = itemView.findViewById(R.id.entry_subtext); + init { + container = itemView.findViewById(R.id.entry_container) + icon = itemView.findViewById(R.id.entry_icon) + text = itemView.findViewById(R.id.entry_text) + subText = itemView.findViewById(R.id.entry_subtext) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.java b/app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.kt similarity index 59% rename from app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.java rename to app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.kt index 4953188bf..b73ca7d91 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.java +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,19 +17,18 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.adapters; +package com.kunzisoft.keepass.adapters -import android.view.View; +import android.view.View -import com.kunzisoft.keepass.R; +import com.kunzisoft.keepass.R -class GroupViewHolder extends BasicViewHolder { +internal class GroupViewHolder(itemView: View) : BasicViewHolder(itemView) { - GroupViewHolder(View itemView) { - super(itemView); - container = itemView.findViewById(R.id.group_container); - icon = itemView.findViewById(R.id.group_icon); - text = itemView.findViewById(R.id.group_text); - subText = itemView.findViewById(R.id.group_subtext); + init { + container = itemView.findViewById(R.id.group_container) + icon = itemView.findViewById(R.id.group_icon) + text = itemView.findViewById(R.id.group_text) + subText = itemView.findViewById(R.id.group_subtext) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java deleted file mode 100644 index aeb4ee340..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.adapters; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.support.annotation.NonNull; -import android.support.v7.util.SortedList; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.util.SortedListAdapterCallback; -import android.util.Log; -import android.view.*; -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.settings.PreferencesUtil; -import com.kunzisoft.keepass.utils.Util; - -public class NodeAdapter extends RecyclerView.Adapter { - private static final String TAG = NodeAdapter.class.getName(); - - private SortedList nodeSortedList; - - private Context context; - private LayoutInflater inflater; - private MenuInflater menuInflater; - private float textSize; - private float subtextSize; - private float iconSize; - private SortNodeEnum listSort; - private boolean groupsBeforeSort; - private boolean ascendingSort; - private boolean showUsernames; - - private NodeClickCallback nodeClickCallback; - private NodeMenuListener nodeMenuListener; - private boolean activateContextMenu; - private boolean readOnly; - private boolean isASearchResult; - - private Database database; - - private int iconGroupColor; - private int iconEntryColor; - - /** - * Create node list adapter with contextMenu or not - * @param context Context to use - */ - public NodeAdapter(final Context context, MenuInflater menuInflater) { - this.inflater = LayoutInflater.from(context); - this.menuInflater = menuInflater; - this.context = context; - assignPreferences(); - this.activateContextMenu = false; - this.readOnly = false; - this.isASearchResult = false; - - this.nodeSortedList = new SortedList<>(NodeVersioned.class, new SortedListAdapterCallback(this) { - @Override public int compare(NodeVersioned item1, NodeVersioned item2) { - return listSort.getNodeComparator(ascendingSort, groupsBeforeSort).compare(item1, item2); - } - - @Override public boolean areContentsTheSame(NodeVersioned oldItem, NodeVersioned newItem) { - return oldItem.getTitle().equals(newItem.getTitle()) - && oldItem.getIcon().equals(newItem.getIcon()); - } - - @Override public boolean areItemsTheSame(NodeVersioned item1, NodeVersioned item2) { - return item1.equals(item2); - } - }); - - // Database - this.database = App.Companion.getCurrentDatabase(); - - // Retrieve the color to tint the icon - int[] attrTextColorPrimary = {android.R.attr.textColorPrimary}; - TypedArray taTextColorPrimary = context.getTheme().obtainStyledAttributes(attrTextColorPrimary); - this.iconGroupColor = taTextColorPrimary.getColor(0, Color.BLACK); - taTextColorPrimary.recycle(); - int[] attrTextColor = {android.R.attr.textColor}; // In two times to fix bug compilation - TypedArray taTextColor = context.getTheme().obtainStyledAttributes(attrTextColor); - this.iconEntryColor = taTextColor.getColor(0, Color.BLACK); - taTextColor.recycle(); - } - - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - } - - public void setIsASearchResult(boolean isASearchResult) { - this.isASearchResult = isASearchResult; - } - - public void setActivateContextMenu(boolean activate) { - this.activateContextMenu = activate; - } - - private void assignPreferences() { - float textSizeDefault = Util.getListTextDefaultSize(context); - this.textSize = PreferencesUtil.getListTextSize(context); - this.subtextSize = context.getResources().getInteger(R.integer.list_small_size_default) - * textSize / textSizeDefault; - // Retrieve the icon size - float iconDefaultSize = context.getResources().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.showUsernames = PreferencesUtil.showUsernamesListEntries(context); - } - - /** - * Rebuild the list by clear and build children from the group - */ - public void rebuildList(GroupVersioned group) { - this.nodeSortedList.clear(); - assignPreferences(); - // TODO verify sort - try { - this.nodeSortedList.addAll(group.getChildrenWithoutMetaStream()); - } catch (Exception e) { - Log.e(TAG, "Can't add node elements to the list", e); - Toast.makeText(context, "Can't add node elements to the list : " + e.getMessage(), Toast.LENGTH_LONG).show(); - } - } - - /** - * Determine if the adapter contains or not any element - * @return true if the list is empty - */ - public boolean isEmpty() { - return nodeSortedList.size() <= 0; - } - - /** - * Add a node to the list - * @param node Node to add - */ - public void addNode(NodeVersioned node) { - nodeSortedList.add(node); - } - - /** - * Remove a node in the list - * @param node Node to delete - */ - public void removeNode(NodeVersioned node) { - nodeSortedList.remove(node); - } - - /** - * Update a node in the list - * @param oldNode Node before the update - * @param newNode Node after the update - */ - public void updateNode(NodeVersioned oldNode, NodeVersioned newNode) { - nodeSortedList.beginBatchedUpdates(); - nodeSortedList.remove(oldNode); - nodeSortedList.add(newNode); - nodeSortedList.endBatchedUpdates(); - } - - /** - * Notify a change sort of the list - */ - public void notifyChangeSort(SortNodeEnum sortNodeEnum, boolean ascending, boolean groupsBefore) { - this.listSort = sortNodeEnum; - this.ascendingSort = ascending; - this.groupsBeforeSort = groupsBefore; - } - - @Override - public int getItemViewType(int position) { - return nodeSortedList.get(position).getType().ordinal(); - } - - @NonNull - @Override - public BasicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - BasicViewHolder basicViewHolder; - View view; - if (viewType == Type.GROUP.ordinal()) { - view = inflater.inflate(R.layout.list_nodes_group, parent, false); - basicViewHolder = new GroupViewHolder(view); - } else { - view = inflater.inflate(R.layout.list_nodes_entry, parent, false); - basicViewHolder = new EntryViewHolder(view); - } - return basicViewHolder; - } - - @Override - public void onBindViewHolder(@NonNull BasicViewHolder holder, int position) { - NodeVersioned subNode = nodeSortedList.get(position); - // Assign image - int iconColor = Color.BLACK; - switch (subNode.getType()) { - case GROUP: - iconColor = iconGroupColor; - break; - case ENTRY: - iconColor = iconEntryColor; - break; - } - database.getDrawFactory().assignDatabaseIconTo(context, holder.icon, subNode.getIcon(), iconColor); - // Assign text - holder.text.setText(subNode.getTitle()); - // Assign click - holder.container.setOnClickListener( - new OnNodeClickListener(subNode)); - // Context menu - if (activateContextMenu) { - holder.container.setOnCreateContextMenuListener( - new ContextMenuBuilder(subNode, nodeMenuListener, readOnly)); - } - - // Add username - holder.subText.setText(""); - holder.subText.setVisibility(View.GONE); - if (subNode.getType().equals(Type.ENTRY)) { - EntryVersioned entry = (EntryVersioned) subNode; - - database.startManageEntry(entry); - - holder.text.setText(entry.getVisualTitle()); - - String username = entry.getUsername(); - if (showUsernames && !username.isEmpty()) { - holder.subText.setVisibility(View.VISIBLE); - holder.subText.setText(username); - } - - database.stopManageEntry(entry); - } - - // Assign image and text size - // Relative size of the icon - holder.icon.getLayoutParams().height = ((int) iconSize); - holder.icon.getLayoutParams().width = ((int) iconSize); - holder.text.setTextSize(textSize); - holder.subText.setTextSize(subtextSize); - } - - @Override - public int getItemCount() { - return nodeSortedList.size(); - } - - /** - * Assign a listener when a node is clicked - */ - public void setOnNodeClickListener(NodeClickCallback nodeClickCallback) { - this.nodeClickCallback = nodeClickCallback; - } - - /** - * Assign a listener when an element of menu is clicked - */ - public void setNodeMenuListener(NodeMenuListener nodeMenuListener) { - this.nodeMenuListener = nodeMenuListener; - } - - /** - * Callback listener to redefine to do an action when a node is click - */ - public interface NodeClickCallback { - void onNodeClick(NodeVersioned node); - } - - /** - * Menu listener to redefine to do an action in menu - */ - public interface NodeMenuListener { - boolean onOpenMenuClick(NodeVersioned node); - boolean onEditMenuClick(NodeVersioned node); - boolean onCopyMenuClick(NodeVersioned node); - boolean onMoveMenuClick(NodeVersioned node); - boolean onDeleteMenuClick(NodeVersioned node); - } - - /** - * Utility class for node listener - */ - private class OnNodeClickListener implements View.OnClickListener { - private NodeVersioned node; - - OnNodeClickListener(NodeVersioned node) { - this.node = node; - } - - @Override - public void onClick(View v) { - if (nodeClickCallback != null) - nodeClickCallback.onNodeClick(node); - } - } - - /** - * Utility class for menu listener - */ - private class ContextMenuBuilder implements View.OnCreateContextMenuListener { - - private NodeVersioned node; - private NodeMenuListener menuListener; - private boolean readOnly; - - ContextMenuBuilder(NodeVersioned node, NodeMenuListener menuListener, boolean readOnly) { - this.menuListener = menuListener; - this.node = node; - this.readOnly = readOnly; - } - - @Override - public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) { - menuInflater.inflate(R.menu.node_menu, contextMenu); - - // Opening - MenuItem menuItem = contextMenu.findItem(R.id.menu_open); - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - - Database database = App.Companion.getCurrentDatabase(); - - // Edition - if (readOnly || node.equals(database.getRecycleBin())) { - contextMenu.removeItem(R.id.menu_edit); - } else { - menuItem = contextMenu.findItem(R.id.menu_edit); - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - } - - // Copy (not for group) - if (readOnly - || isASearchResult - || node.equals(database.getRecycleBin()) - || node.getType().equals(Type.GROUP)) { - // TODO COPY For Group - contextMenu.removeItem(R.id.menu_copy); - } else { - menuItem = contextMenu.findItem(R.id.menu_copy); - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - } - - // Move - if (readOnly - || isASearchResult - || node.equals(database.getRecycleBin())) { - contextMenu.removeItem(R.id.menu_move); - } else { - menuItem = contextMenu.findItem(R.id.menu_move); - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - } - - // Deletion - if (readOnly || node.equals(database.getRecycleBin())) { - contextMenu.removeItem(R.id.menu_delete); - } else { - menuItem = contextMenu.findItem(R.id.menu_delete); - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener); - } - } - - private MenuItem.OnMenuItemClickListener mOnMyActionClickListener = new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - if (menuListener == null) - return false; - switch ( item.getItemId() ) { - case R.id.menu_open: - return menuListener.onOpenMenuClick(node); - case R.id.menu_edit: - return menuListener.onEditMenuClick(node); - case R.id.menu_copy: - return menuListener.onCopyMenuClick(node); - case R.id.menu_move: - return menuListener.onMoveMenuClick(node); - case R.id.menu_delete: - return menuListener.onDeleteMenuClick(node); - default: - return false; - } - } - }; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt new file mode 100644 index 000000000..555504223 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt @@ -0,0 +1,362 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.adapters + +import android.content.Context +import android.graphics.Color +import android.support.v7.util.SortedList +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.util.SortedListAdapterCallback +import android.util.Log +import android.view.* +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.settings.PreferencesUtil +import com.kunzisoft.keepass.utils.Util + +class NodeAdapter +/** + * Create node list adapter with contextMenu or not + * @param context Context to use + */ +(private val context: Context, private val menuInflater: MenuInflater) : RecyclerView.Adapter() { + + private val nodeSortedList: SortedList + private val inflater: LayoutInflater = LayoutInflater.from(context) + 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 showUserNames: Boolean = false + + private var nodeClickCallback: NodeClickCallback? = null + private var nodeMenuListener: NodeMenuListener? = null + private var activateContextMenu: Boolean = false + private var readOnly: Boolean = false + private var isASearchResult: Boolean = false + + private val mDatabase: Database + + private val iconGroupColor: Int + private val iconEntryColor: Int + + /** + * Determine if the adapter contains or not any element + * @return true if the list is empty + */ + val isEmpty: Boolean + get() = nodeSortedList.size() <= 0 + + init { + assignPreferences() + this.activateContextMenu = false + this.readOnly = false + this.isASearchResult = false + + 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 + } + + override fun areContentsTheSame(oldItem: NodeVersioned, newItem: NodeVersioned): Boolean { + return oldItem.title == newItem.title && oldItem.icon == newItem.icon + } + + override fun areItemsTheSame(item1: NodeVersioned, item2: NodeVersioned): Boolean { + return item1 == item2 + } + }) + + // Database + this.mDatabase = App.currentDatabase + + // Retrieve the color to tint the icon + val taTextColorPrimary = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary)) + this.iconGroupColor = taTextColorPrimary.getColor(0, Color.BLACK) + taTextColorPrimary.recycle() + // In two times to fix bug compilation + val taTextColor = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor)) + this.iconEntryColor = taTextColor.getColor(0, Color.BLACK) + taTextColor.recycle() + } + + fun setReadOnly(readOnly: Boolean) { + this.readOnly = readOnly + } + + fun setIsASearchResult(isASearchResult: Boolean) { + this.isASearchResult = isASearchResult + } + + fun setActivateContextMenu(activate: Boolean) { + this.activateContextMenu = activate + } + + private fun assignPreferences() { + val textSizeDefault = Util.getListTextDefaultSize(context) + this.textSize = PreferencesUtil.getListTextSize(context) + this.subtextSize = context.resources.getInteger(R.integer.list_small_size_default) * textSize / textSizeDefault + // Retrieve the icon size + 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.showUserNames = PreferencesUtil.showUsernamesListEntries(context) + } + + /** + * Rebuild the list by clear and build children from the group + */ + fun rebuildList(group: GroupVersioned) { + this.nodeSortedList.clear() + assignPreferences() + // TODO verify sort + try { + this.nodeSortedList.addAll(group.getChildrenWithoutMetaStream()) + } 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() + } + + } + + /** + * Add a node to the list + * @param node Node to add + */ + fun addNode(node: NodeVersioned) { + nodeSortedList.add(node) + } + + /** + * Remove a node in the list + * @param node Node to delete + */ + fun removeNode(node: NodeVersioned) { + nodeSortedList.remove(node) + } + + /** + * Update a node in the list + * @param oldNode Node before the update + * @param newNode Node after the update + */ + fun updateNode(oldNode: NodeVersioned, newNode: NodeVersioned) { + nodeSortedList.beginBatchedUpdates() + nodeSortedList.remove(oldNode) + nodeSortedList.add(newNode) + nodeSortedList.endBatchedUpdates() + } + + /** + * Notify a change sort of the list + */ + fun notifyChangeSort(sortNodeEnum: SortNodeEnum, ascending: Boolean, groupsBefore: Boolean) { + this.listSort = sortNodeEnum + this.ascendingSort = ascending + this.groupsBeforeSort = groupsBefore + } + + override fun getItemViewType(position: Int): Int { + return nodeSortedList.get(position).type.ordinal + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BasicViewHolder { + val basicViewHolder: BasicViewHolder + val view: View + if (viewType == Type.GROUP.ordinal) { + view = inflater.inflate(R.layout.list_nodes_group, parent, false) + basicViewHolder = GroupViewHolder(view) + } else { + view = inflater.inflate(R.layout.list_nodes_entry, parent, false) + basicViewHolder = EntryViewHolder(view) + } + return basicViewHolder + } + + override fun onBindViewHolder(holder: BasicViewHolder, position: Int) { + val subNode = nodeSortedList.get(position) + // Assign image + val iconColor = when (subNode.type) { + Type.GROUP -> iconGroupColor + Type.ENTRY -> iconEntryColor + } + mDatabase.drawFactory.assignDatabaseIconTo(context, holder.icon, subNode.icon, iconColor) + // Assign text + holder.text?.text = subNode.title + // Assign click + holder.container?.setOnClickListener( + OnNodeClickListener(subNode)) + // Context menu + if (activateContextMenu) { + holder.container?.setOnCreateContextMenuListener( + ContextMenuBuilder(subNode, nodeMenuListener, readOnly)) + } + + // Add username + holder.subText?.text = "" + holder.subText?.visibility = View.GONE + if (subNode.type == Type.ENTRY) { + val entry = subNode as EntryVersioned + + mDatabase.startManageEntry(entry) + + holder.text?.text = entry.getVisualTitle() + + val username = entry.username + if (showUserNames && username.isNotEmpty()) { + holder.subText?.visibility = View.VISIBLE + holder.subText?.text = username + } + + mDatabase.stopManageEntry(entry) + } + + // Assign image and text size + // Relative size of the icon + holder.icon?.layoutParams?.height = iconSize.toInt() + holder.icon?.layoutParams?.width = iconSize.toInt() + holder.text?.textSize = textSize + holder.subText?.textSize = subtextSize + } + + override fun getItemCount(): Int { + return nodeSortedList.size() + } + + /** + * Assign a listener when a node is clicked + */ + fun setOnNodeClickListener(nodeClickCallback: NodeClickCallback?) { + this.nodeClickCallback = nodeClickCallback + } + + /** + * Assign a listener when an element of menu is clicked + */ + fun setNodeMenuListener(nodeMenuListener: NodeMenuListener?) { + this.nodeMenuListener = nodeMenuListener + } + + /** + * Callback listener to redefine to do an action when a node is click + */ + interface NodeClickCallback { + fun onNodeClick(node: NodeVersioned) + } + + /** + * Menu listener to redefine to do an action in menu + */ + interface NodeMenuListener { + fun onOpenMenuClick(node: NodeVersioned): Boolean + fun onEditMenuClick(node: NodeVersioned): Boolean + fun onCopyMenuClick(node: NodeVersioned): Boolean + fun onMoveMenuClick(node: NodeVersioned): Boolean + fun onDeleteMenuClick(node: NodeVersioned): Boolean + } + + /** + * Utility class for node listener + */ + private inner class OnNodeClickListener internal constructor(private val node: NodeVersioned) : View.OnClickListener { + + override fun onClick(v: View) { + nodeClickCallback?.onNodeClick(node) + } + } + + /** + * Utility class for menu listener + */ + private inner class ContextMenuBuilder internal constructor(private val node: NodeVersioned, private val menuListener: NodeMenuListener?, private val readOnly: Boolean) : View.OnCreateContextMenuListener { + + private val mOnMyActionClickListener = MenuItem.OnMenuItemClickListener { item -> + if (menuListener == null) + return@OnMenuItemClickListener false + when (item.itemId) { + R.id.menu_open -> menuListener.onOpenMenuClick(node) + R.id.menu_edit -> menuListener.onEditMenuClick(node) + R.id.menu_copy -> menuListener.onCopyMenuClick(node) + R.id.menu_move -> menuListener.onMoveMenuClick(node) + R.id.menu_delete -> menuListener.onDeleteMenuClick(node) + else -> false + } + } + + override fun onCreateContextMenu(contextMenu: ContextMenu, view: View, contextMenuInfo: ContextMenu.ContextMenuInfo) { + menuInflater.inflate(R.menu.node_menu, contextMenu) + + // Opening + var menuItem = contextMenu.findItem(R.id.menu_open) + menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + + val database = App.currentDatabase + + // Edition + if (readOnly || node == database.recycleBin) { + contextMenu.removeItem(R.id.menu_edit) + } else { + menuItem = contextMenu.findItem(R.id.menu_edit) + menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + } + + // Copy (not for group) + if (readOnly + || isASearchResult + || node == database.recycleBin + || node.type == Type.GROUP) { + // TODO COPY For Group + contextMenu.removeItem(R.id.menu_copy) + } else { + menuItem = contextMenu.findItem(R.id.menu_copy) + menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + } + + // Move + if (readOnly + || isASearchResult + || node == database.recycleBin) { + contextMenu.removeItem(R.id.menu_move) + } else { + menuItem = contextMenu.findItem(R.id.menu_move) + menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + } + + // Deletion + if (readOnly || node == database.recycleBin) { + contextMenu.removeItem(R.id.menu_delete) + } else { + menuItem = contextMenu.findItem(R.id.menu_delete) + menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + } + } + } + + companion object { + private val TAG = NodeAdapter::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java deleted file mode 100644 index 7e11dd460..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.adapters; - -import android.content.Context; -import android.content.res.TypedArray; -import android.database.Cursor; -import android.graphics.Color; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.cursor.EntryCursor; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.EntryVersioned; -import com.kunzisoft.keepass.database.element.PwIcon; -import com.kunzisoft.keepass.database.element.PwIconFactory; -import com.kunzisoft.keepass.settings.PreferencesUtil; - -import java.util.UUID; - -public class SearchEntryCursorAdapter extends CursorAdapter { - - private LayoutInflater cursorInflater; - private Database database; - private boolean displayUsername; - private int iconColor; - - public SearchEntryCursorAdapter(Context context, Database database) { - super(context, null, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); - cursorInflater = (LayoutInflater) context.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - this.database = database; - - // Get the icon color - int[] attrTextColor = {R.attr.textColorInverse}; - TypedArray taTextColor = context.getTheme().obtainStyledAttributes(attrTextColor); - this.iconColor = taTextColor.getColor(0, Color.WHITE); - taTextColor.recycle(); - - reInit(context); - } - - public void reInit(Context context) { - this.displayUsername = PreferencesUtil.showUsernamesListEntries(context); - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - - View view = cursorInflater.inflate(R.layout.search_entry, parent ,false); - ViewHolder viewHolder = new ViewHolder(); - viewHolder.imageViewIcon = view.findViewById(R.id.entry_icon); - viewHolder.textViewTitle = view.findViewById(R.id.entry_text); - viewHolder.textViewSubTitle = view.findViewById(R.id.entry_subtext); - view.setTag(viewHolder); - - return view; - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - - // Retrieve elements from cursor - UUID uuid = new UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), - cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))); - PwIconFactory iconFactory = database.getIconFactory(); - PwIcon icon = iconFactory.getIcon( - new UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), - cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))); - if (icon.isUnknown()) { - icon = iconFactory.getIcon(cursor.getInt(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_STANDARD))); - if (icon.isUnknown()) - icon = iconFactory.getKeyIcon(); - } - String title = cursor.getString( cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE) ); - String username = cursor.getString( cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_USERNAME) ); - String url = cursor.getString( cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_URL) ); - - ViewHolder viewHolder = (ViewHolder) view.getTag(); - - // Assign image - database.getDrawFactory().assignDatabaseIconTo(context, viewHolder.imageViewIcon, icon, iconColor); - - // Assign title - String showTitle = EntryVersioned.CREATOR.getVisualTitle(false, title, username, url, uuid.toString()); - viewHolder.textViewTitle.setText(showTitle); - if (displayUsername && !username.isEmpty()) { - viewHolder.textViewSubTitle.setText(String.format("(%s)", username)); - } else { - viewHolder.textViewSubTitle.setText(""); - } - } - - private static class ViewHolder { - ImageView imageViewIcon; - TextView textViewTitle; - TextView textViewSubTitle; - } - - @Override - public Cursor runQueryOnBackgroundThread(CharSequence constraint) { - return database.searchEntry(constraint.toString()); - } - - public EntryVersioned getEntryFromPosition(int position) { - EntryVersioned pwEntry = null; - - Cursor cursor = this.getCursor(); - if (cursor.moveToFirst() - && cursor.move(position)) { - pwEntry = database.getEntryFrom(cursor); - } - return pwEntry; - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt new file mode 100644 index 000000000..a9b7f58c7 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.adapters + +import android.content.Context +import android.database.Cursor +import android.graphics.Color +import android.support.v4.widget.CursorAdapter +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.cursor.EntryCursor +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.PwIcon +import com.kunzisoft.keepass.settings.PreferencesUtil +import java.util.* + +class SearchEntryCursorAdapter(context: Context, private val database: Database) : CursorAdapter(context, null, FLAG_REGISTER_CONTENT_OBSERVER) { + + private val cursorInflater: LayoutInflater = context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + private var displayUsername: Boolean = false + private val iconColor: Int + + init { + // Get the icon color + val taTextColor = context.theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) + this.iconColor = taTextColor.getColor(0, Color.WHITE) + taTextColor.recycle() + + reInit(context) + } + + fun reInit(context: Context) { + this.displayUsername = PreferencesUtil.showUsernamesListEntries(context) + } + + override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View { + + val view = cursorInflater.inflate(R.layout.search_entry, parent, false) + val viewHolder = ViewHolder() + viewHolder.imageViewIcon = view.findViewById(R.id.entry_icon) + viewHolder.textViewTitle = view.findViewById(R.id.entry_text) + viewHolder.textViewSubTitle = view.findViewById(R.id.entry_subtext) + view.tag = viewHolder + + return view + } + + override fun bindView(view: View, context: Context, cursor: Cursor) { + + // Retrieve elements from cursor + val uuid = UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_MOST_SIGNIFICANT_BITS)), + cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_UUID_LEAST_SIGNIFICANT_BITS))) + val iconFactory = database.iconFactory + var icon: PwIcon = iconFactory.getIcon( + UUID(cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_MOST_SIGNIFICANT_BITS)), + cursor.getLong(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_CUSTOM_UUID_LEAST_SIGNIFICANT_BITS)))) + if (icon.isUnknown) { + icon = iconFactory.getIcon(cursor.getInt(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_ICON_STANDARD))) + if (icon.isUnknown) + icon = iconFactory.keyIcon + } + val title = cursor.getString(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_TITLE)) + val username = cursor.getString(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_USERNAME)) + val url = cursor.getString(cursor.getColumnIndex(EntryCursor.COLUMN_INDEX_URL)) + + val viewHolder = view.tag as ViewHolder + + // Assign image + database.drawFactory.assignDatabaseIconTo(context, viewHolder.imageViewIcon, icon, iconColor) + + // Assign title + val showTitle = EntryVersioned.getVisualTitle(false, title, username, url, uuid.toString()) + viewHolder.textViewTitle?.text = showTitle + if (displayUsername && username.isNotEmpty()) { + viewHolder.textViewSubTitle?.text = String.format("(%s)", username) + } else { + viewHolder.textViewSubTitle?.text = "" + } + } + + private class ViewHolder { + internal var imageViewIcon: ImageView? = null + internal var textViewTitle: TextView? = null + internal var textViewSubTitle: TextView? = null + } + + override fun runQueryOnBackgroundThread(constraint: CharSequence): Cursor? { + return database.searchEntry(constraint.toString()) + } + + fun getEntryFromPosition(position: Int): EntryVersioned? { + var pwEntry: EntryVersioned? = null + + val cursor = this.cursor + if (cursor.moveToFirst() && cursor.move(position)) { + pwEntry = database.getEntryFrom(cursor) + } + return pwEntry + } + +} diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java index fa576a400..04c393b58 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java @@ -116,6 +116,7 @@ public class GroupEditDialogFragment extends DialogFragment int[] attrs = {android.R.attr.textColorPrimary}; TypedArray ta = getActivity().getTheme().obtainStyledAttributes(attrs); iconColor = ta.getColor(0, Color.WHITE); + ta.recycle(); // Init elements database = App.Companion.getCurrentDatabase(); diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java index 3f819093c..b7a753ca0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java @@ -139,6 +139,7 @@ public class IconPickerDialogFragment extends DialogFragment { TypedArray ta = getContext().getTheme().obtainStyledAttributes(attrs); int iconColor = ta.getColor(0, Color.BLACK); ImageViewCompat.setImageTintList(iv, ColorStateList.valueOf(iconColor)); + ta.recycle(); } return currView; diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt index cc38c8bcf..0be3664f4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt @@ -81,7 +81,7 @@ class EntryContentsView @JvmOverloads constructor(context: Context, attrs: Attri val attrColorAccent = intArrayOf(R.attr.colorAccentCompat) val taColorAccent = context.theme.obtainStyledAttributes(attrColorAccent) - this.colorAccent = taColorAccent.getColor(0, Color.BLACK) + colorAccent = taColorAccent.getColor(0, Color.BLACK) taColorAccent.recycle() } From f36849e815748db705d518aaaaf8f130e35a6351 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 21:39:27 +0200 Subject: [PATCH 140/289] Kotlinized Autofill and BackupAgent --- .../keepass/autofill/AutofillHelper.kt | 45 +++---- .../keepass/autofill/KeeAutofillService.java | 87 ------------- .../keepass/autofill/KeeAutofillService.kt | 71 +++++++++++ .../keepass/autofill/StructureParser.java | 115 ------------------ .../keepass/autofill/StructureParser.kt | 113 +++++++++++++++++ ...ackupAgent.java => SettingsBackupAgent.kt} | 27 ++-- 6 files changed, 221 insertions(+), 237 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt rename app/src/main/java/com/kunzisoft/keepass/backup/{SettingsBackupAgent.java => SettingsBackupAgent.kt} (53%) diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt index 7eaf623d4..97ecaca8c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -52,11 +52,11 @@ object AutofillHelper { } private fun makeEntryTitle(entry: EntryVersioned): String { - if (!entry.title.isEmpty() && !entry.username.isEmpty()) + if (entry.title.isNotEmpty() && entry.username.isNotEmpty()) return String.format("%s (%s)", entry.title, entry.username) - if (!entry.title.isEmpty()) + if (entry.title.isNotEmpty()) return entry.title - if (!entry.username.isEmpty()) + if (entry.username.isNotEmpty()) return entry.username return if (!entry.notes.isEmpty()) entry.notes.trim { it <= ' ' } else "" // TODO No title @@ -89,23 +89,26 @@ object AutofillHelper { * Method to hit when right key is selected */ fun buildResponseWhenEntrySelected(activity: Activity, entry: EntryVersioned) { - val mReplyIntent: Intent - activity.intent?.let { intent -> - if (intent.extras.containsKey(ASSIST_STRUCTURE)) { - val structure = intent.getParcelableExtra(ASSIST_STRUCTURE) - val result = StructureParser(structure).parse() - - // New Response - val responseBuilder = FillResponse.Builder() - val dataset = buildDataset(activity, entry, result) - responseBuilder.addDataset(dataset) - mReplyIntent = Intent() - Log.d(activity.javaClass.name, "Successed Autofill auth.") - mReplyIntent.putExtra( - AutofillManager.EXTRA_AUTHENTICATION_RESULT, - responseBuilder.build()) - activity.setResult(Activity.RESULT_OK, mReplyIntent) - } else { + var setResultOk = false + activity.intent?.extras?.let { extras -> + if (extras.containsKey(ASSIST_STRUCTURE)) { + activity.intent?.getParcelableExtra(ASSIST_STRUCTURE)?.let { structure -> + StructureParser(structure).parse()?.let { result -> + // New Response + val responseBuilder = FillResponse.Builder() + val dataset = buildDataset(activity, entry, result) + responseBuilder.addDataset(dataset) + val mReplyIntent = Intent() + Log.d(activity.javaClass.name, "Successed Autofill auth.") + mReplyIntent.putExtra( + AutofillManager.EXTRA_AUTHENTICATION_RESULT, + responseBuilder.build()) + setResultOk = true + activity.setResult(Activity.RESULT_OK, mReplyIntent) + } + } + } + if (!setResultOk) { Log.w(activity.javaClass.name, "Failed Autofill auth.") activity.setResult(Activity.RESULT_CANCELED) } @@ -118,7 +121,7 @@ object AutofillHelper { fun startActivityForAutofillResult(activity: Activity, intent: Intent, assistStructure: AssistStructure) { EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent) intent.putExtra(ASSIST_STRUCTURE, assistStructure) - activity.startActivityForResult(intent, AutofillHelper.AUTOFILL_RESPONSE_REQUEST_CODE) + activity.startActivityForResult(intent, AUTOFILL_RESPONSE_REQUEST_CODE) } /** diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java deleted file mode 100644 index eb62f6a48..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2017 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.autofill; - -import android.app.assist.AssistStructure; -import android.content.IntentSender; -import android.os.Build; -import android.os.CancellationSignal; -import android.service.autofill.AutofillService; -import android.service.autofill.FillCallback; -import android.service.autofill.FillContext; -import android.service.autofill.FillRequest; -import android.service.autofill.FillResponse; -import android.service.autofill.SaveCallback; -import android.service.autofill.SaveRequest; -import android.support.annotation.NonNull; -import android.support.annotation.RequiresApi; -import android.util.Log; -import android.view.autofill.AutofillId; -import android.widget.RemoteViews; - -import com.kunzisoft.keepass.R; - -import java.util.Arrays; -import java.util.List; - -@RequiresApi(api = Build.VERSION_CODES.O) -public class KeeAutofillService extends AutofillService { - private static final String TAG = "KeeAutofillService"; - - @Override - public void onFillRequest(@NonNull FillRequest request, @NonNull CancellationSignal cancellationSignal, - @NonNull FillCallback callback) { - List fillContexts = request.getFillContexts(); - AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure(); - - cancellationSignal.setOnCancelListener(() -> - Log.e(TAG, "Cancel autofill not implemented in this sample.") - ); - - FillResponse.Builder responseBuilder = new FillResponse.Builder(); - // Check user's settings for authenticating Responses and Datasets. - StructureParser.Result parseResult = new StructureParser(latestStructure).parse(); - AutofillId[] autofillIds = parseResult.allAutofillIds(); - if (!Arrays.asList(autofillIds).isEmpty()) { - // If the entire Autofill Response is authenticated, AuthActivity is used - // to generate Response. - IntentSender sender = AutoFillLauncherActivity.Companion.getAuthIntentSenderForResponse(this); - RemoteViews presentation = new RemoteViews(getPackageName(), R.layout.autofill_service_unlock); - responseBuilder.setAuthentication(autofillIds, sender, presentation); - callback.onSuccess(responseBuilder.build()); - } - } - - @Override - public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) { - // TODO Save autofill - //callback.onFailure(getString(R.string.autofill_not_support_save)); - } - - @Override - public void onConnected() { - Log.d(TAG, "onConnected"); - } - - @Override - public void onDisconnected() { - Log.d(TAG, "onDisconnected"); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt new file mode 100644 index 000000000..baa50588c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.autofill + +import android.os.Build +import android.os.CancellationSignal +import android.service.autofill.* +import android.support.annotation.RequiresApi +import android.util.Log +import android.widget.RemoteViews +import com.kunzisoft.keepass.R + +@RequiresApi(api = Build.VERSION_CODES.O) +class KeeAutofillService : AutofillService() { + + override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal, + callback: FillCallback) { + val fillContexts = request.fillContexts + val latestStructure = fillContexts[fillContexts.size - 1].structure + + cancellationSignal.setOnCancelListener { Log.e(TAG, "Cancel autofill not implemented in this sample.") } + + val responseBuilder = FillResponse.Builder() + // Check user's settings for authenticating Responses and Datasets. + val parseResult = StructureParser(latestStructure).parse() + parseResult?.allAutofillIds()?.let { autofillIds -> + if (listOf(*autofillIds).isNotEmpty()) { + // If the entire Autofill Response is authenticated, AuthActivity is used + // to generate Response. + val sender = AutoFillLauncherActivity.getAuthIntentSenderForResponse(this) + val presentation = RemoteViews(packageName, R.layout.autofill_service_unlock) + responseBuilder.setAuthentication(autofillIds, sender, presentation) + callback.onSuccess(responseBuilder.build()) + } + } + } + + override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { + // TODO Save autofill + //callback.onFailure(getString(R.string.autofill_not_support_save)); + } + + override fun onConnected() { + Log.d(TAG, "onConnected") + } + + override fun onDisconnected() { + Log.d(TAG, "onDisconnected") + } + + companion object { + private val TAG = KeeAutofillService::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.java b/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.java deleted file mode 100644 index e3af9fbcc..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.autofill; - -import android.app.assist.AssistStructure; -import android.os.Build; -import android.support.annotation.RequiresApi; -import android.text.InputType; -import android.util.Log; -import android.view.View; -import android.view.autofill.AutofillId; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - - -/** - * Parse AssistStructure and guess username and password fields. - */ -@RequiresApi(api = Build.VERSION_CODES.O) -class StructureParser { - static private final String TAG = StructureParser.class.getName(); - - final private AssistStructure structure; - private Result result; - private AutofillId usernameCandidate; - - StructureParser(AssistStructure structure) { - this.structure = structure; - } - - Result parse() { - result = new Result(); - usernameCandidate = null; - for (int i=0; i 0) { - if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_USERNAME::equals)) - result.username.add(node.getAutofillId()); - else if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_EMAIL_ADDRESS::equals)) - result.email.add(node.getAutofillId()); - else if (Arrays.stream(hints).anyMatch(View.AUTOFILL_HINT_PASSWORD::equals)) - result.password.add(node.getAutofillId()); - else - Log.d(TAG, "unsupported hints"); - } else if (node.getAutofillType() == View.AUTOFILL_TYPE_TEXT) { - int inputType = node.getInputType(); - if ((inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) > 0) - result.email.add(node.getAutofillId()); - else if ((inputType & InputType.TYPE_TEXT_VARIATION_PASSWORD) > 0) - result.password.add(node.getAutofillId()); - else if (result.password.isEmpty()) - usernameCandidate = node.getAutofillId(); - } - - for (int i=0; i title; - final List webDomain; - final List username; - final List email; - final List password; - - private Result() { - title = new ArrayList<>(); - webDomain = new ArrayList<>(); - username = new ArrayList<>(); - email = new ArrayList<>(); - password = new ArrayList<>(); - } - - AutofillId[] allAutofillIds() { - ArrayList all = new ArrayList<>(); - all.addAll(username); - all.addAll(email); - all.addAll(password); - return all.toArray(new AutofillId[0]); - } - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt new file mode 100644 index 000000000..7b95b069a --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + */ +package com.kunzisoft.keepass.autofill + +import android.app.assist.AssistStructure +import android.os.Build +import android.support.annotation.RequiresApi +import android.text.InputType +import android.util.Log +import android.view.View +import android.view.autofill.AutofillId +import java.util.* + + +/** + * Parse AssistStructure and guess username and password fields. + */ +@RequiresApi(api = Build.VERSION_CODES.O) +internal class StructureParser(private val structure: AssistStructure) { + private var result: Result? = null + private var usernameCandidate: AutofillId? = null + + fun parse(): Result? { + result = Result() + result?.apply { + usernameCandidate = null + for (i in 0 until structure.windowNodeCount) { + val windowNode = structure.getWindowNodeAt(i) + title.add(windowNode.title) + windowNode.rootViewNode.webDomain?.let { + webDomain.add(it) + } + parseViewNode(windowNode.rootViewNode) + } + // If not explicit username field found, add the field just before password field. + if (username.isEmpty() && email.isEmpty() + && password.isNotEmpty() && usernameCandidate != null) + username.add(usernameCandidate!!) + } + + return result + } + + private fun parseViewNode(node: AssistStructure.ViewNode) { + val hints = node.autofillHints + val autofillId = node.autofillId + if (autofillId != null) { + if (hints != null && hints.isNotEmpty()) { + when { + Arrays.stream(hints).anyMatch { View.AUTOFILL_HINT_USERNAME == it } -> result?.username?.add(autofillId) + Arrays.stream(hints).anyMatch { View.AUTOFILL_HINT_EMAIL_ADDRESS == it } -> result?.email?.add(autofillId) + Arrays.stream(hints).anyMatch { View.AUTOFILL_HINT_PASSWORD == it } -> result?.password?.add(autofillId) + else -> Log.d(TAG, "unsupported hints") + } + } else if (node.autofillType == View.AUTOFILL_TYPE_TEXT) { + val inputType = node.inputType + when { + inputType and InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS > 0 -> result?.email?.add(autofillId) + inputType and InputType.TYPE_TEXT_VARIATION_PASSWORD > 0 -> result?.password?.add(autofillId) + result?.password?.isEmpty() == true -> usernameCandidate = autofillId + } + } + } + + for (i in 0 until node.childCount) + parseViewNode(node.getChildAt(i)) + } + + @RequiresApi(api = Build.VERSION_CODES.O) + internal class Result { + val title: MutableList + val webDomain: MutableList + val username: MutableList + val email: MutableList + val password: MutableList + + init { + title = ArrayList() + webDomain = ArrayList() + username = ArrayList() + email = ArrayList() + password = ArrayList() + } + + fun allAutofillIds(): Array { + val all = ArrayList() + all.addAll(username) + all.addAll(email) + all.addAll(password) + return all.toTypedArray() + } + } + + companion object { + private val TAG = StructureParser::class.java.name + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.java b/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.kt similarity index 53% rename from app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.java rename to app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.kt index a770898f7..5adeadddc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.java +++ b/app/src/main/java/com/kunzisoft/keepass/backup/SettingsBackupAgent.kt @@ -1,5 +1,5 @@ /* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,23 +17,22 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.backup; +package com.kunzisoft.keepass.backup -import android.annotation.SuppressLint; -import android.app.backup.BackupAgentHelper; -import android.app.backup.SharedPreferencesBackupHelper; +import android.annotation.SuppressLint +import android.app.backup.BackupAgentHelper +import android.app.backup.SharedPreferencesBackupHelper @SuppressLint("NewApi") -public class SettingsBackupAgent extends BackupAgentHelper { +class SettingsBackupAgent : BackupAgentHelper() { - private static final String PREFS_BACKUP_KEY = "prefs"; - - @Override - public void onCreate() { - String defaultPrefs = this.getPackageName() + "_preferences"; - - SharedPreferencesBackupHelper prefHelper = new SharedPreferencesBackupHelper(this, defaultPrefs); - addHelper(PREFS_BACKUP_KEY, prefHelper); + override fun onCreate() { + val defaultPrefs = this.packageName + "_preferences" + val prefHelper = SharedPreferencesBackupHelper(this, defaultPrefs) + addHelper(PREFS_BACKUP_KEY, prefHelper) } + companion object { + private const val PREFS_BACKUP_KEY = "prefs" + } } From 63f261f169e0d841ca081d0da65275ec9703726a Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 9 Jul 2019 17:12:00 +0200 Subject: [PATCH 141/289] Fix add new entry --- .../java/com/kunzisoft/keepass/activities/GroupActivity.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 117235011..f985e8d2c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -209,8 +209,8 @@ class GroupActivity : LockingActivity(), .show(supportFragmentManager, GroupEditDialogFragment.TAG_CREATE_GROUP) } - mCurrentGroup?.let { currentGroup -> - addNodeButtonView?.setAddEntryClickListener { + addNodeButtonView?.setAddEntryClickListener { + mCurrentGroup?.let { currentGroup -> EntryEditActivity.launch(this@GroupActivity, currentGroup) } } @@ -971,7 +971,6 @@ class GroupActivity : LockingActivity(), } override fun onBackPressed() { - // Normal way when we are not in root if (mRootGroup != null && mRootGroup != mCurrentGroup) super.onBackPressed() From 7ad43dbed78f799f27271db89560f5cf8ec04352 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 10 Jul 2019 17:47:06 +0200 Subject: [PATCH 142/289] Kotlinized Activities --- app/build.gradle | 5 +- app/src/main/AndroidManifest.xml | 6 +- .../keepass/activities/AboutActivity.kt | 2 +- .../activities/FileDatabaseSelectActivity.kt | 557 ++++++++++ .../keepass/activities/ListNodesFragment.kt | 2 +- .../keepass/activities/PasswordActivity.kt | 895 ++++++++++++++++ .../activities/lock/LockingActivity.kt | 2 +- .../stylish}/FilePickerStylishActivity.java | 3 +- .../{ => activities}/stylish/Stylish.java | 2 +- .../stylish/StylishActivity.java | 2 +- .../stylish/StylishFragment.java | 2 +- .../utilities}/UriIntentInitTask.kt | 6 +- .../utilities/UriIntentInitTaskCallback.kt} | 8 +- .../kunzisoft/keepass/adapters/NodeAdapter.kt | 57 +- .../java/com/kunzisoft/keepass/app/App.kt | 2 +- .../autofill/AutoFillLauncherActivity.kt | 2 +- .../dialogs/CreateFileDialogFragment.java | 2 +- .../dialogs/IconPickerDialogFragment.java | 2 +- .../DeleteFileHistoryAsyncTask.java | 54 - .../fileselect/DeleteFileHistoryAsyncTask.kt | 42 + .../FileDatabaseHistoryAdapter.java | 181 ---- .../fileselect/FileDatabaseHistoryAdapter.kt | 156 +++ .../keepass/fileselect/FileDatabaseModel.java | 2 +- .../FileDatabaseSelectActivity.java | 602 ----------- .../fileselect/OpenFileHistoryAsyncTask.java | 52 - ...wHolder.kt => OpenFileHistoryAsyncTask.kt} | 28 +- .../database/FileDatabaseHistory.kt | 5 +- .../magikeyboard/KeyboardLauncherActivity.kt | 2 +- .../NotificationCopyingService.java | 2 +- .../keepass/password/IntentBuildLauncher.java | 7 - .../keepass/password/PasswordActivity.java | 998 ------------------ .../keepass/settings/MagikIMESettings.java | 2 +- .../settings/NestedSettingsFragment.java | 2 +- .../com/kunzisoft/keepass/utils/MenuUtil.kt | 2 +- 34 files changed, 1730 insertions(+), 1964 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt rename app/src/main/java/com/kunzisoft/keepass/{fileselect => activities/stylish}/FilePickerStylishActivity.java (96%) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/stylish/Stylish.java (98%) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/stylish/StylishActivity.java (96%) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/stylish/StylishFragment.java (98%) rename app/src/main/java/com/kunzisoft/keepass/{password => activities/utilities}/UriIntentInitTask.kt (94%) rename app/src/main/java/com/kunzisoft/keepass/{password/UriIntentInitTaskCallback.java => activities/utilities/UriIntentInitTaskCallback.kt} (79%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java rename app/src/main/java/com/kunzisoft/keepass/fileselect/{FileDatabaseHistoryViewHolder.kt => OpenFileHistoryAsyncTask.kt} (51%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/password/IntentBuildLauncher.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java diff --git a/app/build.gradle b/app/build.gradle index 10482c38c..24fb0a3e3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' android { compileSdkVersion 27 @@ -80,7 +81,7 @@ android { def supportVersion = "27.1.1" def spongycastleVersion = "1.58.0.0" -def permissionDispatcherVersion = "3.1.0" +def permissionDispatcherVersion = "3.3.1" dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" @@ -103,7 +104,7 @@ dependencies { // if you don't use android.app.Fragment you can exclude support for them exclude module: "support-v13" } - annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcherVersion" + kapt "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcherVersion" // Apache Commons Collections implementation 'commons-collections:commons-collections:3.2.1' implementation 'org.apache.commons:commons-io:1.3.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 399f808f7..3789cf299 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,7 +27,7 @@ android:value="" /> @@ -89,7 +89,7 @@ android:resource="@xml/nnf_provider_paths" /> diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt index c2018c06c..6593f3719 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt @@ -28,7 +28,7 @@ import android.widget.TextView import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.stylish.StylishActivity +import com.kunzisoft.keepass.activities.stylish.StylishActivity import org.joda.time.DateTime diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt new file mode 100644 index 000000000..100f7ba71 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -0,0 +1,557 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.activities + +import android.Manifest +import android.app.Activity +import android.app.assist.AssistStructure +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.os.Handler +import android.preference.PreferenceManager +import android.support.annotation.RequiresApi +import android.support.v7.app.AlertDialog +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.Toolbar +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.EditText +import android.widget.TextView +import android.widget.Toast +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.stylish.StylishActivity +import com.kunzisoft.keepass.autofill.AutofillHelper +import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable +import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable +import com.kunzisoft.keepass.database.action.ProgressDialogRunnable +import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment +import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment +import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation +import com.kunzisoft.keepass.fileselect.* +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory +import com.kunzisoft.keepass.magikeyboard.KeyboardHelper +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.utils.EmptyUtils +import com.kunzisoft.keepass.utils.MenuUtil +import com.kunzisoft.keepass.utils.UriUtil +import net.cachapa.expandablelayout.ExpandableLayout +import permissions.dispatcher.* +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import java.lang.ref.WeakReference +import java.net.URLDecoder +import java.util.* + +@RuntimePermissions +class FileDatabaseSelectActivity : StylishActivity(), + CreateFileDialogFragment.DefinePathDialogListener, + AssignMasterKeyDialogFragment.AssignPasswordDialogListener, + FileDatabaseHistoryAdapter.FileItemOpenListener, + FileDatabaseHistoryAdapter.FileSelectClearListener, + FileDatabaseHistoryAdapter.FileInformationShowListener { + + // Views + private var fileListContainer: View? = null + private var createButtonView: View? = null + private var browseButtonView: View? = null + private var openButtonView: View? = null + private var fileSelectExpandableButtonView: View? = null + private var fileSelectExpandableLayout: ExpandableLayout? = null + private var openFileNameView: EditText? = null + + // Adapter to manage database history list + private var mAdapterDatabaseHistory: FileDatabaseHistoryAdapter? = null + + private var mFileDatabaseHistory: FileDatabaseHistory? = null + + private var mDatabaseFileUri: Uri? = null + + private var mKeyFileHelper: KeyFileHelper? = null + + private var mDefaultPath: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + mFileDatabaseHistory = FileDatabaseHistory.getInstance(WeakReference(applicationContext)) + + setContentView(R.layout.file_selection) + fileListContainer = findViewById(R.id.container_file_list) + + val toolbar = findViewById(R.id.toolbar) + toolbar.title = "" + setSupportActionBar(toolbar) + + openFileNameView = findViewById(R.id.file_filename) + + // Set the initial value of the filename + mDefaultPath = (Environment.getExternalStorageDirectory().absolutePath + + getString(R.string.database_file_path_default) + + getString(R.string.database_file_name_default) + + getString(R.string.database_file_extension_default)) + openFileNameView?.setHint(R.string.open_link_database) + + // Button to expand file selection + fileSelectExpandableButtonView = findViewById(R.id.file_select_expandable_button) + fileSelectExpandableLayout = findViewById(R.id.file_select_expandable) + fileSelectExpandableButtonView?.setOnClickListener { _ -> + if (fileSelectExpandableLayout?.isExpanded == true) + fileSelectExpandableLayout?.collapse() + else + fileSelectExpandableLayout?.expand() + } + + // History list + val databaseFileListView = findViewById(R.id.file_list) + databaseFileListView.layoutManager = LinearLayoutManager(this) + + // Open button + openButtonView = findViewById(R.id.open_database) + openButtonView?.setOnClickListener { _ -> + var fileName = openFileNameView?.text?.toString() ?: "" + mDefaultPath?.let { + if (fileName.isEmpty()) + fileName = it + } + launchPasswordActivityWithPath(fileName) + } + + // Create button + createButtonView = findViewById(R.id.create_database) + createButtonView?.setOnClickListener { openCreateFileDialogFragmentWithPermissionCheck() } + + mKeyFileHelper = KeyFileHelper(this) + browseButtonView = findViewById(R.id.browse_button) + browseButtonView?.setOnClickListener(mKeyFileHelper!!.getOpenFileOnClickViewListener { Uri.parse("file://" + openFileNameView!!.text.toString()) }) + + // Construct adapter with listeners + mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this@FileDatabaseSelectActivity, + mFileDatabaseHistory?.databaseUriList ?: ArrayList()) + mAdapterDatabaseHistory?.setOnItemClickListener(this) + mAdapterDatabaseHistory?.setFileSelectClearListener(this) + mAdapterDatabaseHistory?.setFileInformationShowListener(this) + databaseFileListView.adapter = mAdapterDatabaseHistory + + // Load default database if not an orientation change + if (!(savedInstanceState != null + && savedInstanceState.containsKey(EXTRA_STAY) + && savedInstanceState.getBoolean(EXTRA_STAY, false))) { + val prefs = PreferenceManager.getDefaultSharedPreferences(this) + val fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "") + + if (fileName!!.isNotEmpty()) { + val dbUri = UriUtil.parseDefaultFile(fileName) + var scheme: String? = null + if (dbUri != null) + scheme = dbUri.scheme + + if (!EmptyUtils.isNullOrEmpty(scheme) && scheme!!.equals("file", ignoreCase = true)) { + val path = dbUri!!.path + val db = File(path!!) + + if (db.exists()) { + launchPasswordActivityWithPath(path) + } + } else { + if (dbUri != null) + launchPasswordActivityWithPath(dbUri.toString()) + } + } + } + + Handler().post { performedNextEducation(FileDatabaseSelectActivityEducation(this)) } + } + + private fun performedNextEducation(fileDatabaseSelectActivityEducation: FileDatabaseSelectActivityEducation) { + // If no recent files + if (createButtonView != null + && mFileDatabaseHistory != null + && !mFileDatabaseHistory!!.hasRecentFiles() && fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation( + createButtonView!!, + { + openCreateFileDialogFragmentWithPermissionCheck() + }, + { + // But if the user cancel, it can also select a database + performedNextEducation(fileDatabaseSelectActivityEducation) + })) + else if (browseButtonView != null + && fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation( + browseButtonView!!, + {tapTargetView -> + mKeyFileHelper?.openFileOnClickViewListener?.onClick(tapTargetView) + }, + { + fileSelectExpandableButtonView?.let { + fileDatabaseSelectActivityEducation + .checkAndPerformedOpenLinkDatabaseEducation(it) + } + } + )) + ; + } + + private fun fileNoFoundAction(e: FileNotFoundException) { + val error = getString(R.string.file_not_found_content) + Toast.makeText(this@FileDatabaseSelectActivity, + error, Toast.LENGTH_LONG).show() + Log.e(TAG, error, e) + } + + private fun launchPasswordActivity(fileName: String, keyFile: String) { + EntrySelectionHelper.doEntrySelectionAction(intent, + { + try { + PasswordActivity.launch(this@FileDatabaseSelectActivity, + fileName, keyFile) + } catch (e: FileNotFoundException) { + fileNoFoundAction(e) + } + }, + { + try { + PasswordActivity.launchForKeyboardResult(this@FileDatabaseSelectActivity, + fileName, keyFile) + finish() + } catch (e: FileNotFoundException) { + fileNoFoundAction(e) + } + }, + { assistStructure -> + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, + fileName, keyFile, + assistStructure) + } catch (e: FileNotFoundException) { + fileNoFoundAction(e) + } + + } + }) + } + + private fun launchPasswordActivityWithPath(path: String) { + launchPasswordActivity(path, "") + // Delete flickering for kitkat <= + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + overridePendingTransition(0, 0) + } + + private fun updateExternalStorageWarning() { + // To show errors + var warning = -1 + val state = Environment.getExternalStorageState() + if (state == Environment.MEDIA_MOUNTED_READ_ONLY) { + warning = R.string.read_only_warning + } else if (state != Environment.MEDIA_MOUNTED) { + warning = R.string.warning_unmounted + } + + val labelWarningView = findViewById(R.id.label_warning) + if (warning != -1) { + labelWarningView.setText(warning) + labelWarningView.visibility = View.VISIBLE + } else { + labelWarningView.visibility = View.INVISIBLE + } + } + + override fun onResume() { + super.onResume() + + updateExternalStorageWarning() + updateFileListVisibility() + mAdapterDatabaseHistory!!.notifyDataSetChanged() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + // only to keep the current activity + outState.putBoolean(EXTRA_STAY, true) + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + // NOTE: delegate the permission handling to generated method + onRequestPermissionsResult(requestCode, grantResults) + } + + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + fun openCreateFileDialogFragment() { + val createFileDialogFragment = CreateFileDialogFragment() + createFileDialogFragment.show(supportFragmentManager, "createFileDialogFragment") + } + + private fun updateFileListVisibility() { + if (mAdapterDatabaseHistory?.itemCount == 0) + fileListContainer?.visibility = View.INVISIBLE + else + fileListContainer?.visibility = View.VISIBLE + } + + /** + * Create file for database + * @return If not created, return false + */ + private fun createDatabaseFile(path: Uri): Boolean { + + val pathString = URLDecoder.decode(path.path) + // Make sure file name exists + if (pathString.isEmpty()) { + Log.e(TAG, getString(R.string.error_filename_required)) + Toast.makeText(this@FileDatabaseSelectActivity, + R.string.error_filename_required, + Toast.LENGTH_LONG).show() + return false + } + + // Try to create the file + val file = File(pathString) + try { + if (file.exists()) { + Log.e(TAG, getString(R.string.error_database_exists) + " " + file) + Toast.makeText(this@FileDatabaseSelectActivity, + R.string.error_database_exists, + Toast.LENGTH_LONG).show() + return false + } + val parent = file.parentFile + + if (parent == null || parent.exists() && !parent.isDirectory) { + Log.e(TAG, getString(R.string.error_invalid_path) + " " + file) + Toast.makeText(this@FileDatabaseSelectActivity, + R.string.error_invalid_path, + Toast.LENGTH_LONG).show() + return false + } + + if (!parent.exists()) { + // Create parent directory + if (!parent.mkdirs()) { + Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent) + Toast.makeText(this@FileDatabaseSelectActivity, + R.string.error_could_not_create_parent, + Toast.LENGTH_LONG).show() + return false + } + } + + return file.createNewFile() + } catch (e: IOException) { + Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.localizedMessage) + e.printStackTrace() + Toast.makeText( + this@FileDatabaseSelectActivity, + getText(R.string.error_file_not_create).toString() + " " + + e.localizedMessage, + Toast.LENGTH_LONG).show() + return false + } + + } + + override fun onDefinePathDialogPositiveClick(pathFile: Uri): Boolean { + mDatabaseFileUri = pathFile + return if (createDatabaseFile(pathFile)) { + AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog") + true + } else + false + } + + override fun onDefinePathDialogNegativeClick(pathFile: Uri): Boolean { + return true + } + + override fun onAssignKeyDialogPositiveClick( + masterPasswordChecked: Boolean, masterPassword: String?, + keyFileChecked: Boolean, keyFile: Uri?) { + + try { + mDatabaseFileUri?.path?.let { databaseFilename -> + // Create the new database and start prof + Thread(ProgressDialogRunnable(this, + R.string.progress_create + ) { + CreateDatabaseRunnable(databaseFilename) { database -> + // TODO store database created + AssignPasswordInDatabaseRunnable(this@FileDatabaseSelectActivity, + database, + masterPasswordChecked, + masterPassword, + keyFileChecked, + keyFile, + true, // TODO get readonly + LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename)) + ) + } + }).start() + } + } catch (e: Exception) { + val error = "Unable to create database with this password and key file" + Toast.makeText(this, error, Toast.LENGTH_LONG).show() + Log.e(TAG, error + " " + e.message) + // TODO remove + e.printStackTrace() + } + + } + + private inner class LaunchGroupActivityFinish internal constructor(private val fileURI: Uri) : ActionRunnable() { + + override fun run() { + finishRun(true, null) + } + + override fun onFinishRun(isSuccess: Boolean, message: String?) { + runOnUiThread { + if (isSuccess) { + // Add database to recent files + mFileDatabaseHistory?.addDatabaseUri(fileURI) + mAdapterDatabaseHistory?.notifyDataSetChanged() + updateFileListVisibility() + GroupActivity.launch(this@FileDatabaseSelectActivity) + } else { + Log.e(TAG, "Unable to open the database") + } + } + } + } + + override fun onAssignKeyDialogNegativeClick( + masterPasswordChecked: Boolean, masterPassword: String?, + keyFileChecked: Boolean, keyFile: Uri?) { + + } + + override fun onFileItemOpenListener(itemPosition: Int) { + OpenFileHistoryAsyncTask({ fileName, keyFile -> + if (fileName != null && keyFile != null) + launchPasswordActivity(fileName, keyFile) + updateFileListVisibility() + }, mFileDatabaseHistory).execute(itemPosition) + } + + override fun onClickFileInformation(fileDatabaseModel: FileDatabaseModel) { + FileInformationDialogFragment.newInstance(fileDatabaseModel).show(supportFragmentManager, "fileInformation") + } + + override fun onFileSelectClearListener(fileDatabaseModel: FileDatabaseModel): Boolean { + DeleteFileHistoryAsyncTask({ + mFileDatabaseHistory?.deleteDatabaseUri(fileDatabaseModel.fileUri) + mAdapterDatabaseHistory?.notifyDataSetChanged() + updateFileListVisibility() + }, mFileDatabaseHistory, mAdapterDatabaseHistory).execute(fileDatabaseModel) + return true + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + super.onActivityResult(requestCode, resultCode, data) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) + } + + mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data + ) { uri -> + if (uri != null) { + if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) { + launchPasswordActivityWithPath(uri.toString()) + } else { + fileSelectExpandableLayout?.expand(false) + openFileNameView?.setText(uri.toString()) + } + } + } + } + + @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + internal fun showRationaleForExternalStorage(request: PermissionRequest) { + AlertDialog.Builder(this) + .setMessage(R.string.permission_external_storage_rationale_write_database) + .setPositiveButton(R.string.allow) { _, _ -> request.proceed() } + .setNegativeButton(R.string.cancel) { _, _ -> request.cancel() } + .show() + } + + @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) + internal fun showDeniedForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show() + } + + @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE) + internal fun showNeverAskForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + MenuUtil.defaultMenuInflater(menuInflater, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item) + } + + companion object { + + private const val TAG = "FileDbSelectActivity" + private const val EXTRA_STAY = "EXTRA_STAY" + + /* + * ------------------------- + * No Standard Launch, pass by PasswordActivity + * ------------------------- + */ + + /* + * ------------------------- + * Keyboard Launch + * ------------------------- + */ + + fun launchForKeyboardSelection(activity: Activity) { + KeyboardHelper.startActivityForKeyboardSelection(activity, Intent(activity, FileDatabaseSelectActivity::class.java)) + } + + /* + * ------------------------- + * Autofill Launch + * ------------------------- + */ + + @RequiresApi(api = Build.VERSION_CODES.O) + fun launchForAutofillResult(activity: Activity, assistStructure: AssistStructure) { + AutofillHelper.startActivityForAutofillResult(activity, + Intent(activity, FileDatabaseSelectActivity::class.java), + assistStructure) + } + } +} 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 23bd0b8a0..7e8b00b5d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt @@ -22,7 +22,7 @@ import com.kunzisoft.keepass.database.element.GroupVersioned import com.kunzisoft.keepass.database.element.NodeVersioned import com.kunzisoft.keepass.dialogs.SortDialogFragment import com.kunzisoft.keepass.settings.PreferencesUtil -import com.kunzisoft.keepass.stylish.StylishFragment +import com.kunzisoft.keepass.activities.stylish.StylishFragment class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionListener { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt new file mode 100644 index 000000000..bfede2351 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -0,0 +1,895 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.activities + +import android.Manifest +import android.app.Activity +import android.app.assist.AssistStructure +import android.app.backup.BackupManager +import android.content.DialogInterface +import android.content.Intent +import android.content.SharedPreferences +import android.hardware.fingerprint.FingerprintManager +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.preference.PreferenceManager +import android.support.annotation.RequiresApi +import android.support.v7.app.AlertDialog +import android.support.v7.widget.Toolbar +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.* +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.lock.LockingActivity +import com.kunzisoft.keepass.activities.stylish.StylishActivity +import com.kunzisoft.keepass.activities.utilities.UriIntentInitTask +import com.kunzisoft.keepass.activities.utilities.UriIntentInitTaskCallback +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.ProgressDialogRunnable +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper +import com.kunzisoft.keepass.education.PasswordActivityEducation +import com.kunzisoft.keepass.fileselect.KeyFileHelper +import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector +import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog +import com.kunzisoft.keepass.fingerprint.FingerPrintHelper +import com.kunzisoft.keepass.magikeyboard.KeyboardHelper +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.tasks.ActionRunnable +import com.kunzisoft.keepass.utils.EmptyUtils +import com.kunzisoft.keepass.utils.MenuUtil +import com.kunzisoft.keepass.utils.UriUtil +import permissions.dispatcher.* +import java.io.File +import java.io.FileNotFoundException +import java.lang.ref.WeakReference + +@RuntimePermissions +class PasswordActivity : StylishActivity(), + FingerPrintHelper.FingerPrintCallback, + UriIntentInitTaskCallback { + + // Views + private var toolbar: Toolbar? = null + private var fingerprintContainerView: View? = null + private var fingerPrintAnimatedVector: FingerPrintAnimatedVector? = null + private var fingerprintTextView: TextView? = null + private var fingerprintImageView: ImageView? = null + private var filenameView: TextView? = null + private var passwordView: EditText? = null + private var keyFileView: EditText? = null + private var confirmButtonView: Button? = null + private var checkboxPasswordView: CompoundButton? = null + private var checkboxKeyFileView: CompoundButton? = null + private var checkboxDefaultDatabaseView: CompoundButton? = null + + private var enableButtonOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null + + private var mDatabaseFileUri: Uri? = null + private var prefs: SharedPreferences? = null + private var prefsNoBackup: SharedPreferences? = null + + private var mRememberKeyFile: Boolean = false + private var mKeyFileHelper: KeyFileHelper? = null + + private var readOnly: Boolean = false + + private var fingerPrintHelper: FingerPrintHelper? = null + private var fingerprintMustBeConfigured = true + private var fingerPrintMode: FingerPrintHelper.Mode? = null + + // makes it possible to store passwords per database + private val preferenceKeyValue: String + get() = PREF_KEY_VALUE_PREFIX + (mDatabaseFileUri?.path ?: "") + + private val preferenceKeyIvSpec: String + get() = PREF_KEY_IV_PREFIX + (mDatabaseFileUri?.path ?: "") + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + prefs = PreferenceManager.getDefaultSharedPreferences(this) + prefsNoBackup = PreferencesUtil.getNoBackupSharedPreferences(applicationContext) + + mRememberKeyFile = prefs!!.getBoolean(getString(R.string.keyfile_key), + resources.getBoolean(R.bool.keyfile_default)) + + setContentView(R.layout.password) + + toolbar = findViewById(R.id.toolbar) + toolbar?.title = getString(R.string.app_name) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + + confirmButtonView = findViewById(R.id.pass_ok) + filenameView = findViewById(R.id.filename) + passwordView = findViewById(R.id.password) + keyFileView = findViewById(R.id.pass_keyfile) + checkboxPasswordView = findViewById(R.id.password_checkbox) + checkboxKeyFileView = findViewById(R.id.keyfile_checkox) + checkboxDefaultDatabaseView = findViewById(R.id.default_database) + + readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState) + + val browseView = findViewById(R.id.browse_button) + mKeyFileHelper = KeyFileHelper(this@PasswordActivity) + browseView.setOnClickListener(mKeyFileHelper!!.openFileOnClickViewListener) + + passwordView?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun afterTextChanged(editable: Editable) { + if (editable.toString().isNotEmpty() && checkboxPasswordView?.isChecked != true) + checkboxPasswordView?.isChecked = true + } + }) + keyFileView?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} + + override fun afterTextChanged(editable: Editable) { + if (editable.toString().isNotEmpty() && checkboxKeyFileView?.isChecked != true) + checkboxKeyFileView?.isChecked = true + } + }) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + fingerprintContainerView = findViewById(R.id.fingerprint_container) + fingerprintTextView = findViewById(R.id.fingerprint_label) + fingerprintImageView = findViewById(R.id.fingerprint_image) + initForFingerprint() + // Init the fingerprint animation + fingerPrintAnimatedVector = FingerPrintAnimatedVector(this, + fingerprintImageView!!) + } + } + + override fun onResume() { + // If the database isn't accessible make sure to clear the password field, if it + // was saved in the instance state + if (App.currentDatabase.loaded) { + setEmptyViews() + } + + // 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 + } + enableButtonOnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { _, isChecked -> + if (!PreferencesUtil.emptyPasswordAllowed(this@PasswordActivity)) { + confirmButtonView?.isEnabled = isChecked + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // Check if fingerprint well init (be called the first time the fingerprint is configured + // and the activity still active) + if (fingerPrintHelper == null || !fingerPrintHelper!!.isFingerprintInitialized) { + initForFingerprint() + } + + // Start the animation in all cases + fingerPrintAnimatedVector?.startScan() + } else { + checkboxPasswordView?.setOnCheckedChangeListener(enableButtonOnCheckedChangeListener) + } + + UriIntentInitTask(WeakReference(this), this, mRememberKeyFile) + .execute(intent) + } + + override fun onSaveInstanceState(outState: Bundle) { + ReadOnlyHelper.onSaveInstanceState(outState, readOnly) + super.onSaveInstanceState(outState) + } + + override fun onPostInitTask(dbUri: Uri?, keyFileUri: Uri?, errorStringId: Int?) { + mDatabaseFileUri = dbUri + + if (errorStringId != null) { + Toast.makeText(this@PasswordActivity, errorStringId, Toast.LENGTH_LONG).show() + finish() + return + } + + // Verify permission to read file + if (mDatabaseFileUri != null && !mDatabaseFileUri!!.scheme!!.contains("content")) + doNothingWithPermissionCheck() + + // Define title + val dbUriString = mDatabaseFileUri?.toString() ?: "" + if (dbUriString.isNotEmpty()) { + if (PreferencesUtil.isFullFilePathEnable(this)) + filenameView?.text = dbUriString + else + filenameView?.text = File(mDatabaseFileUri!!.path!!).name // TODO Encapsulate + } + + // Define Key File text + val keyUriString = keyFileUri?.toString() ?: "" + if (keyUriString.isNotEmpty() && mRememberKeyFile) { // Bug KeepassDX #18 + populateKeyFileTextView(keyUriString) + } + + // Define listeners for default database checkbox and validate button + checkboxDefaultDatabaseView?.setOnCheckedChangeListener { _, isChecked -> + var newDefaultFileName = "" + if (isChecked) { + newDefaultFileName = mDatabaseFileUri?.toString() ?: newDefaultFileName + } + + prefs?.edit()?.apply() { + putString(KEY_DEFAULT_FILENAME, newDefaultFileName) + apply() + } + + val backupManager = BackupManager(this@PasswordActivity) + backupManager.dataChanged() + } + confirmButtonView?.setOnClickListener { verifyAllViewsAndLoadDatabase() } + + // Retrieve settings for default database + val defaultFilename = prefs?.getString(KEY_DEFAULT_FILENAME, "") + if (mDatabaseFileUri != null + && !EmptyUtils.isNullOrEmpty(mDatabaseFileUri!!.path) + && UriUtil.equalsDefaultfile(mDatabaseFileUri, defaultFilename)) { + checkboxDefaultDatabaseView?.isChecked = true + } + + // checks if fingerprint is available, will also start listening for fingerprints when available + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + checkFingerprintAvailability() + } + + // If Activity is launch with a password and want to open directly + val intent = intent + val password = intent.getStringExtra(KEY_PASSWORD) + val launchImmediately = intent.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false) + if (password != null) { + populatePasswordTextView(password) + } + if (launchImmediately) { + verifyCheckboxesAndLoadDatabase(password, keyFileUri) + } + } + + private fun setEmptyViews() { + populatePasswordTextView(null) + // Bug KeepassDX #18 + if (!mRememberKeyFile) { + populateKeyFileTextView(null) + } + } + + private fun populatePasswordTextView(text: String?) { + if (text == null || text.isEmpty()) { + passwordView?.setText("") + if (checkboxPasswordView?.isChecked == true) + checkboxPasswordView?.isChecked = false + } else { + passwordView?.setText(text) + if (checkboxPasswordView?.isChecked != true) + checkboxPasswordView?.isChecked = true + } + } + + private fun populateKeyFileTextView(text: String?) { + if (text == null || text.isEmpty()) { + keyFileView?.setText("") + if (checkboxKeyFileView?.isChecked == true) + checkboxKeyFileView?.isChecked = false + } else { + keyFileView?.setText(text) + if (checkboxKeyFileView?.isChecked != true) + checkboxKeyFileView?.isChecked = true + } + } + + // fingerprint related code here + @RequiresApi(api = Build.VERSION_CODES.M) + private fun initForFingerprint() { + fingerPrintMode = FingerPrintHelper.Mode.NOT_CONFIGURED_MODE + + fingerPrintHelper = FingerPrintHelper(this, this) + + checkboxPasswordView?.setOnCheckedChangeListener { compoundButton, checked -> + if (!fingerprintMustBeConfigured) { + // encrypt or decrypt mode based on how much input or not + if (checked) { + toggleFingerprintMode(FingerPrintHelper.Mode.STORE_MODE) + } else { + if (prefsNoBackup?.contains(preferenceKeyValue) == true) { + toggleFingerprintMode(FingerPrintHelper.Mode.OPEN_MODE) + } else { + // This happens when no fingerprints are registered. + toggleFingerprintMode(FingerPrintHelper.Mode.WAITING_PASSWORD_MODE) + } + } + } + + // Add old listener to enable the button, only be call here because of onCheckedChange bug + enableButtonOnCheckedChangeListener?.onCheckedChanged(compoundButton, checked) + } + + // callback for fingerprint findings + fingerPrintHelper?.setAuthenticationCallback(object : FingerprintManager.AuthenticationCallback() { + override fun onAuthenticationError( + errorCode: Int, + errString: CharSequence) { + when (errorCode) { + 5 -> Log.i(TAG, "Fingerprint authentication error. Code : $errorCode Error : $errString") + else -> { + Log.e(TAG, "Fingerprint authentication error. Code : $errorCode Error : $errString") + setFingerPrintView(errString.toString(), true) + } + } + } + + override fun onAuthenticationHelp( + helpCode: Int, + helpString: CharSequence) { + Log.w(TAG, "Fingerprint authentication help. Code : $helpCode Help : $helpString") + showError(helpString) + setFingerPrintView(helpString.toString(), true) + fingerprintTextView?.text = helpString + } + + override fun onAuthenticationFailed() { + Log.e(TAG, "Fingerprint authentication failed, fingerprint not recognized") + showError(R.string.fingerprint_not_recognized) + } + + override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) { + when (fingerPrintMode) { + FingerPrintHelper.Mode.STORE_MODE -> { + // newly store the entered password in encrypted way + fingerPrintHelper?.encryptData(passwordView?.text.toString()) + } + FingerPrintHelper.Mode.OPEN_MODE -> { + // retrieve the encrypted value from preferences + prefsNoBackup?.getString(preferenceKeyValue, null)?.let { + fingerPrintHelper?.decryptData(it) + } + } + } + } + }) + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private fun initEncryptData() { + setFingerPrintView(R.string.store_with_fingerprint) + fingerPrintMode = FingerPrintHelper.Mode.STORE_MODE + fingerPrintHelper?.initEncryptData() + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private fun initDecryptData() { + setFingerPrintView(R.string.scanning_fingerprint) + fingerPrintMode = FingerPrintHelper.Mode.OPEN_MODE + if (fingerPrintHelper != null) { + prefsNoBackup?.getString(preferenceKeyIvSpec, null)?.let { + fingerPrintHelper?.initDecryptData(it) + } + } + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private fun initWaitData() { + setFingerPrintView(R.string.no_password_stored, true) + fingerPrintMode = FingerPrintHelper.Mode.WAITING_PASSWORD_MODE + } + + @RequiresApi(api = Build.VERSION_CODES.M) + @Synchronized + private fun toggleFingerprintMode(newMode: FingerPrintHelper.Mode) { + when (newMode) { + FingerPrintHelper.Mode.WAITING_PASSWORD_MODE -> setFingerPrintView(R.string.no_password_stored, true) + FingerPrintHelper.Mode.STORE_MODE -> setFingerPrintView(R.string.store_with_fingerprint) + FingerPrintHelper.Mode.OPEN_MODE -> setFingerPrintView(R.string.scanning_fingerprint) + else -> {} + } + if (newMode != fingerPrintMode) { + fingerPrintMode = newMode + reInitWithFingerprintMode() + } + } + + @RequiresApi(api = Build.VERSION_CODES.M) + @Synchronized + private fun reInitWithFingerprintMode() { + when (fingerPrintMode) { + FingerPrintHelper.Mode.STORE_MODE -> initEncryptData() + FingerPrintHelper.Mode.WAITING_PASSWORD_MODE -> initWaitData() + FingerPrintHelper.Mode.OPEN_MODE -> initDecryptData() + else -> {} + } + // Show fingerprint key deletion + invalidateOptionsMenu() + } + + override fun onPause() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + fingerPrintAnimatedVector?.stopScan() + // stop listening when we go in background + fingerPrintMode = FingerPrintHelper.Mode.NOT_CONFIGURED_MODE + fingerPrintHelper?.stopListening() + } + super.onPause() + } + + private fun setFingerPrintVisibility(vis: Int) { + runOnUiThread { fingerprintContainerView?.visibility = vis } + } + + private fun setFingerPrintView(textId: Int, lock: Boolean = false) { + setFingerPrintView(getString(textId), lock) + } + + private fun setFingerPrintView(text: CharSequence, lock: Boolean) { + runOnUiThread { + fingerprintContainerView?.alpha = if (lock) 0.8f else 1f + fingerprintTextView?.text = text + } + } + + @RequiresApi(api = Build.VERSION_CODES.M) + @Synchronized + private fun checkFingerprintAvailability() { + // fingerprint not supported (by API level or hardware) so keep option hidden + // or manually disable + if (!PreferencesUtil.isFingerprintEnable(applicationContext) + || !FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager::class.java))) { + setFingerPrintVisibility(View.GONE) + } else { + // show explanations + fingerprintContainerView?.setOnClickListener { _ -> + FingerPrintExplanationDialog().show(supportFragmentManager, "fingerprintDialog") + } + setFingerPrintVisibility(View.VISIBLE) + + if (fingerPrintHelper?.hasEnrolledFingerprints() != true) { + // This happens when no fingerprints are registered. Listening won't start + setFingerPrintView(R.string.configure_fingerprint, true) + } else { + fingerprintMustBeConfigured = false + + // fingerprint available but no stored password found yet for this DB so show info don't listen + if (prefsNoBackup?.contains(preferenceKeyValue) != true) { + if (checkboxPasswordView?.isChecked == true) { + // listen for encryption + initEncryptData() + } else { + // wait for typing + initWaitData() + } + } else { + // listen for decryption + initDecryptData() + }// all is set here so we can confirm to user and start listening for fingerprints + }// finally fingerprint available and configured so we can use it + }// fingerprint is available but not configured show icon but in disabled state with some information + + // Show fingerprint key deletion + invalidateOptionsMenu() + } + + private fun removePrefsNoBackupKey() { + prefsNoBackup?.edit() + ?.remove(preferenceKeyValue) + ?.remove(preferenceKeyIvSpec) + ?.apply() + } + + override fun handleEncryptedResult( + value: String, + ivSpec: String) { + prefsNoBackup?.edit() + ?.putString(preferenceKeyValue, value) + ?.putString(preferenceKeyIvSpec, ivSpec) + ?.apply() + verifyAllViewsAndLoadDatabase() + setFingerPrintView(R.string.encrypted_value_stored) + } + + override fun handleDecryptedResult(passwordValue: String) { + // Load database directly + verifyKeyFileViewsAndLoadDatabase(passwordValue) + } + + @RequiresApi(api = Build.VERSION_CODES.M) + override fun onInvalidKeyException(e: Exception) { + showError(getString(R.string.fingerprint_invalid_key)) + deleteEntryKey() + } + + @RequiresApi(api = Build.VERSION_CODES.M) + override fun onFingerPrintException(e: Exception) { + // Don't show error here; + // showError(getString(R.string.fingerprint_error, e.getMessage())); + // Can be uninit in Activity and init in fragment + setFingerPrintView(e.localizedMessage, true) + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private fun deleteEntryKey() { + fingerPrintHelper?.deleteEntryKey() + removePrefsNoBackupKey() + fingerPrintMode = FingerPrintHelper.Mode.NOT_CONFIGURED_MODE + checkFingerprintAvailability() + } + + private fun showError(messageId: Int) { + showError(getString(messageId)) + } + + private fun showError(message: CharSequence) { + runOnUiThread { Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT).show() } + } + + private fun verifyAllViewsAndLoadDatabase() { + verifyCheckboxesAndLoadDatabase( + passwordView?.text.toString(), + UriUtil.parseDefaultFile(keyFileView?.text.toString())) + } + + private fun verifyCheckboxesAndLoadDatabase(password: String?, keyFile: Uri?) { + var pass = password + var keyF = keyFile + if (checkboxPasswordView?.isChecked != true) { + pass = null + } + if (checkboxKeyFileView?.isChecked != true) { + keyF = null + } + loadDatabase(pass, keyF) + } + + private fun verifyKeyFileViewsAndLoadDatabase(password: String) { + val key = keyFileView?.text.toString() + var keyUri = UriUtil.parseDefaultFile(key) + if (checkboxKeyFileView?.isChecked != true) { + keyUri = null + } + loadDatabase(password, keyUri) + } + + private fun loadDatabase(password: String?, keyFile: Uri?) { + // Clear before we load + val database = App.currentDatabase + database.closeAndClear(applicationContext) + + mDatabaseFileUri?.let { databaseUri -> + // Show the progress dialog and load the database + Thread(ProgressDialogRunnable( + this, + R.string.loading_database + ) { progressTaskUpdater -> + LoadDatabaseRunnable( + WeakReference(this@PasswordActivity), + database, + databaseUri, + password, + keyFile, + progressTaskUpdater, + AfterLoadingDatabase(database)) + }).start() + } + } + + /** + * Called after verify and try to opening the database + */ + private inner class AfterLoadingDatabase internal constructor(var database: Database) : ActionRunnable() { + + override fun onFinishRun(isSuccess: Boolean, message: String?) { + runOnUiThread { + // Recheck fingerprint if error + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // Stay with the same mode + reInitWithFingerprintMode() + } + + if (database.isPasswordEncodingError) { + val dialog = PasswordEncodingDialogHelper() + dialog.show(this@PasswordActivity, + DialogInterface.OnClickListener { _, _ -> launchGroupActivity() }) + } else if (isSuccess) { + launchGroupActivity() + } else { + if (message != null && message.isNotEmpty()) { + Toast.makeText(this@PasswordActivity, message, Toast.LENGTH_LONG).show() + } + } + } + } + } + + private fun launchGroupActivity() { + EntrySelectionHelper.doEntrySelectionAction(intent, + { + GroupActivity.launch(this@PasswordActivity, readOnly) + }, + { + GroupActivity.launchForKeyboardSelection(this@PasswordActivity, readOnly) + // Do not keep history + finish() + }, + { assistStructure -> + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + GroupActivity.launchForAutofillResult(this@PasswordActivity, assistStructure, readOnly) + } + }) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val inflater = menuInflater + // Read menu + inflater.inflate(R.menu.open_file, menu) + changeOpenFileReadIcon(menu.findItem(R.id.menu_open_file_read_mode_key)) + + MenuUtil.defaultMenuInflater(inflater, menu) + + // Fingerprint menu + if (!fingerprintMustBeConfigured && prefsNoBackup?.contains(preferenceKeyValue) == true) + inflater.inflate(R.menu.fingerprint, menu) + + super.onCreateOptionsMenu(menu) + + // Show education views + Handler().post { performedNextEducation(PasswordActivityEducation(this), menu) } + + return true + } + + private fun performedNextEducation(passwordActivityEducation: PasswordActivityEducation, + menu: Menu) { + if (toolbar != null + && passwordActivityEducation.checkAndPerformedFingerprintUnlockEducation( + toolbar!!, + { + performedNextEducation(passwordActivityEducation, menu) + }, + { + performedNextEducation(passwordActivityEducation, menu) + })) + else if (toolbar != null + && toolbar!!.findViewById(R.id.menu_open_file_read_mode_key) != null + && passwordActivityEducation.checkAndPerformedReadOnlyEducation( + toolbar!!.findViewById(R.id.menu_open_file_read_mode_key), + { + onOptionsItemSelected(menu.findItem(R.id.menu_open_file_read_mode_key)) + performedNextEducation(passwordActivityEducation, menu) + }, + { + performedNextEducation(passwordActivityEducation, menu) + })) + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + && PreferencesUtil.isFingerprintEnable(applicationContext) + && FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager::class.java)) + && passwordActivityEducation.checkAndPerformedFingerprintEducation(fingerprintImageView!!)) + ; + } + + private fun changeOpenFileReadIcon(togglePassword: MenuItem) { + if (readOnly) { + togglePassword.setTitle(R.string.menu_file_selection_read_only) + togglePassword.setIcon(R.drawable.ic_read_only_white_24dp) + } else { + togglePassword.setTitle(R.string.menu_open_file_read_and_write) + togglePassword.setIcon(R.drawable.ic_read_write_white_24dp) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + + when (item.itemId) { + android.R.id.home -> finish() + R.id.menu_open_file_read_mode_key -> { + readOnly = !readOnly + changeOpenFileReadIcon(item) + } + R.id.menu_fingerprint_remove_key -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + deleteEntryKey() + } + else -> return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) + } + + return super.onOptionsItemSelected(item) + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + // NOTE: delegate the permission handling to generated method + onRequestPermissionsResult(requestCode, grantResults) + } + + override fun onActivityResult( + requestCode: Int, + resultCode: Int, + data: Intent) { + super.onActivityResult(requestCode, resultCode, data) + + // To get entry in result + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) + } + + var keyFileResult = false + mKeyFileHelper?.let { + keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data + ) { uri -> + if (uri != null) { + populateKeyFileTextView(uri.toString()) + } + } + } + if (!keyFileResult) { + // this block if not a key file response + when (resultCode) { + LockingActivity.RESULT_EXIT_LOCK, Activity.RESULT_CANCELED -> { + setEmptyViews() + App.currentDatabase.closeAndClear(applicationContext) + } + } + } + } + + @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) + fun doNothing() { + } + + @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) + internal fun showRationaleForExternalStorage(request: PermissionRequest) { + AlertDialog.Builder(this) + .setMessage(R.string.permission_external_storage_rationale_read_database) + .setPositiveButton(R.string.allow) { _, _ -> request.proceed() } + .setNegativeButton(R.string.cancel) { _, _ -> request.cancel() } + .show() + } + + @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) + internal fun showDeniedForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show() + finish() + } + + @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE) + internal fun showNeverAskForExternalStorage() { + Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show() + finish() + } + + companion object { + + private val TAG = PasswordActivity::class.java.name + + const val KEY_DEFAULT_FILENAME = "defaultFileName" + + private const val KEY_PASSWORD = "password" + private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately" + private const val PREF_KEY_VALUE_PREFIX = "valueFor_" // key is a combination of db file name and this prefix + private const val PREF_KEY_IV_PREFIX = "ivFor_" // key is a combination of db file name and this prefix + + private fun buildAndLaunchIntent(activity: Activity, fileName: String, keyFile: String, + intentBuildLauncher: (Intent) -> Unit) { + val intent = Intent(activity, PasswordActivity::class.java) + intent.putExtra(UriIntentInitTask.KEY_FILENAME, fileName) + intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile) + intentBuildLauncher.invoke(intent) + } + + @Throws(FileNotFoundException::class) + private fun verifyFileNameUriFromLaunch(fileName: String) { + if (EmptyUtils.isNullOrEmpty(fileName)) { + throw FileNotFoundException() + } + + val uri = UriUtil.parseDefaultFile(fileName) + val scheme = uri.scheme + + if (!EmptyUtils.isNullOrEmpty(scheme) && scheme.equals("file", ignoreCase = true)) { + val dbFile = File(uri.path!!) + if (!dbFile.exists()) { + throw FileNotFoundException() + } + } + } + + /* + * ------------------------- + * Standard Launch + * ------------------------- + */ + + @Throws(FileNotFoundException::class) + fun launch( + activity: Activity, + fileName: String, + keyFile: String) { + verifyFileNameUriFromLaunch(fileName) + buildAndLaunchIntent(activity, fileName, keyFile) { activity.startActivity(it) } + } + + /* + * ------------------------- + * Keyboard Launch + * ------------------------- + */ + + @Throws(FileNotFoundException::class) + fun launchForKeyboardResult( + activity: Activity, + fileName: String, + keyFile: String) { + verifyFileNameUriFromLaunch(fileName) + + buildAndLaunchIntent(activity, fileName, keyFile) { intent -> + KeyboardHelper.startActivityForKeyboardSelection(activity, intent) + } + } + + /* + * ------------------------- + * Autofill Launch + * ------------------------- + */ + + @RequiresApi(api = Build.VERSION_CODES.O) + @Throws(FileNotFoundException::class) + fun launchForAutofillResult( + activity: Activity, + fileName: String, + keyFile: String, + assistStructure: AssistStructure?) { + verifyFileNameUriFromLaunch(fileName) + + if (assistStructure != null) { + buildAndLaunchIntent(activity, fileName, keyFile) { intent -> + AutofillHelper.startActivityForAutofillResult( + activity, + intent, + assistStructure) + } + } else { + launch(activity, fileName, keyFile) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt index dfd61bc85..63b6b8f8f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/lock/LockingActivity.kt @@ -32,7 +32,7 @@ import com.kunzisoft.keepass.activities.EntrySelectionHelper import com.kunzisoft.keepass.activities.ReadOnlyHelper import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.settings.PreferencesUtil -import com.kunzisoft.keepass.stylish.StylishActivity +import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.timeout.TimeoutHelper abstract class LockingActivity : StylishActivity() { diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FilePickerStylishActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/FilePickerStylishActivity.java similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/fileselect/FilePickerStylishActivity.java rename to app/src/main/java/com/kunzisoft/keepass/activities/stylish/FilePickerStylishActivity.java index 2f5a4569d..afd056c21 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FilePickerStylishActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/FilePickerStylishActivity.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.fileselect; +package com.kunzisoft.keepass.activities.stylish; import android.content.Context; import android.os.Bundle; @@ -26,7 +26,6 @@ import android.support.annotation.StyleRes; import android.util.Log; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.stylish.Stylish; import com.nononsenseapps.filepicker.FilePickerActivity; /** diff --git a/app/src/main/java/com/kunzisoft/keepass/stylish/Stylish.java b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/Stylish.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/stylish/Stylish.java rename to app/src/main/java/com/kunzisoft/keepass/activities/stylish/Stylish.java index 12e59035e..44bf11e46 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stylish/Stylish.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/Stylish.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.stylish; +package com.kunzisoft.keepass.activities.stylish; import android.content.Context; import android.support.annotation.StyleRes; diff --git a/app/src/main/java/com/kunzisoft/keepass/stylish/StylishActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.java similarity index 96% rename from app/src/main/java/com/kunzisoft/keepass/stylish/StylishActivity.java rename to app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.java index ba193be46..dd8b3dbd1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stylish/StylishActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.stylish; +package com.kunzisoft.keepass.activities.stylish; import android.os.Bundle; import android.support.annotation.Nullable; diff --git a/app/src/main/java/com/kunzisoft/keepass/stylish/StylishFragment.java b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishFragment.java similarity index 98% rename from app/src/main/java/com/kunzisoft/keepass/stylish/StylishFragment.java rename to app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishFragment.java index 78824839c..0538ef020 100644 --- a/app/src/main/java/com/kunzisoft/keepass/stylish/StylishFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishFragment.java @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.stylish; +package com.kunzisoft.keepass.activities.stylish; import android.content.Context; import android.content.res.TypedArray; diff --git a/app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTask.kt b/app/src/main/java/com/kunzisoft/keepass/activities/utilities/UriIntentInitTask.kt similarity index 94% rename from app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTask.kt rename to app/src/main/java/com/kunzisoft/keepass/activities/utilities/UriIntentInitTask.kt index c82b8a500..a7a8c4957 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTask.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/utilities/UriIntentInitTask.kt @@ -1,4 +1,4 @@ -package com.kunzisoft.keepass.password +package com.kunzisoft.keepass.activities.utilities import android.content.Context import android.content.Intent @@ -13,7 +13,7 @@ import com.kunzisoft.keepass.utils.UriUtil import java.io.File import java.lang.ref.WeakReference -internal class UriIntentInitTask(private val weakContext: WeakReference, +class UriIntentInitTask(private val weakContext: WeakReference, private val uriIntentInitTaskCallback: UriIntentInitTaskCallback, private val isKeyFileNeeded: Boolean) : AsyncTask() { @@ -36,7 +36,7 @@ internal class UriIntentInitTask(private val weakContext: WeakReference } else if (incoming.scheme == "file") { val fileName = incoming.path - if (fileName!!.isEmpty()) { + if (fileName?.isNotEmpty() == true) { // No file name return R.string.file_not_found } diff --git a/app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTaskCallback.java b/app/src/main/java/com/kunzisoft/keepass/activities/utilities/UriIntentInitTaskCallback.kt similarity index 79% rename from app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTaskCallback.java rename to app/src/main/java/com/kunzisoft/keepass/activities/utilities/UriIntentInitTaskCallback.kt index 07f11e59e..016556d05 100644 --- a/app/src/main/java/com/kunzisoft/keepass/password/UriIntentInitTaskCallback.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/utilities/UriIntentInitTaskCallback.kt @@ -1,5 +1,5 @@ /* - * Copyright 2018 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * @@ -17,10 +17,10 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.password; +package com.kunzisoft.keepass.activities.utilities -import android.net.Uri; +import android.net.Uri interface UriIntentInitTaskCallback { - void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId); + fun onPostInitTask(dbUri: Uri?, keyFileUri: Uri?, errorStringId: Int?) } 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 555504223..eb5de13ef 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt @@ -39,7 +39,8 @@ class NodeAdapter * Create node list adapter with contextMenu or not * @param context Context to use */ -(private val context: Context, private val menuInflater: MenuInflater) : RecyclerView.Adapter() { +(private val context: Context, private val menuInflater: MenuInflater) + : RecyclerView.Adapter() { private val nodeSortedList: SortedList private val inflater: LayoutInflater = LayoutInflater.from(context) @@ -208,12 +209,11 @@ class NodeAdapter // Assign text holder.text?.text = subNode.title // Assign click - holder.container?.setOnClickListener( - OnNodeClickListener(subNode)) + holder.container?.setOnClickListener { nodeClickCallback?.onNodeClick(subNode) } // Context menu if (activateContextMenu) { holder.container?.setOnCreateContextMenuListener( - ContextMenuBuilder(subNode, nodeMenuListener, readOnly)) + ContextMenuBuilder(menuInflater, subNode, readOnly, isASearchResult, nodeMenuListener)) } // Add username @@ -279,20 +279,15 @@ class NodeAdapter fun onDeleteMenuClick(node: NodeVersioned): Boolean } - /** - * Utility class for node listener - */ - private inner class OnNodeClickListener internal constructor(private val node: NodeVersioned) : View.OnClickListener { - - override fun onClick(v: View) { - nodeClickCallback?.onNodeClick(node) - } - } - /** * Utility class for menu listener */ - private inner class ContextMenuBuilder internal constructor(private val node: NodeVersioned, private val menuListener: NodeMenuListener?, private val readOnly: Boolean) : View.OnCreateContextMenuListener { + private class ContextMenuBuilder(val menuInflater: MenuInflater, + val node: NodeVersioned, + val readOnly: Boolean, + val isASearchResult: Boolean, + val menuListener: NodeMenuListener?) + : View.OnCreateContextMenuListener { private val mOnMyActionClickListener = MenuItem.OnMenuItemClickListener { item -> if (menuListener == null) @@ -307,21 +302,23 @@ class NodeAdapter } } - override fun onCreateContextMenu(contextMenu: ContextMenu, view: View, contextMenuInfo: ContextMenu.ContextMenuInfo) { + override fun onCreateContextMenu(contextMenu: ContextMenu?, + view: View?, + contextMenuInfo: ContextMenu.ContextMenuInfo?) { menuInflater.inflate(R.menu.node_menu, contextMenu) // Opening - var menuItem = contextMenu.findItem(R.id.menu_open) - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + var menuItem = contextMenu?.findItem(R.id.menu_open) + menuItem?.setOnMenuItemClickListener(mOnMyActionClickListener) val database = App.currentDatabase // Edition if (readOnly || node == database.recycleBin) { - contextMenu.removeItem(R.id.menu_edit) + contextMenu?.removeItem(R.id.menu_edit) } else { - menuItem = contextMenu.findItem(R.id.menu_edit) - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + menuItem = contextMenu?.findItem(R.id.menu_edit) + menuItem?.setOnMenuItemClickListener(mOnMyActionClickListener) } // Copy (not for group) @@ -330,28 +327,28 @@ class NodeAdapter || node == database.recycleBin || node.type == Type.GROUP) { // TODO COPY For Group - contextMenu.removeItem(R.id.menu_copy) + contextMenu?.removeItem(R.id.menu_copy) } else { - menuItem = contextMenu.findItem(R.id.menu_copy) - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + menuItem = contextMenu?.findItem(R.id.menu_copy) + menuItem?.setOnMenuItemClickListener(mOnMyActionClickListener) } // Move if (readOnly || isASearchResult || node == database.recycleBin) { - contextMenu.removeItem(R.id.menu_move) + contextMenu?.removeItem(R.id.menu_move) } else { - menuItem = contextMenu.findItem(R.id.menu_move) - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + menuItem = contextMenu?.findItem(R.id.menu_move) + menuItem?.setOnMenuItemClickListener(mOnMyActionClickListener) } // Deletion if (readOnly || node == database.recycleBin) { - contextMenu.removeItem(R.id.menu_delete) + contextMenu?.removeItem(R.id.menu_delete) } else { - menuItem = contextMenu.findItem(R.id.menu_delete) - menuItem.setOnMenuItemClickListener(mOnMyActionClickListener) + menuItem = contextMenu?.findItem(R.id.menu_delete) + menuItem?.setOnMenuItemClickListener(mOnMyActionClickListener) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.kt b/app/src/main/java/com/kunzisoft/keepass/app/App.kt index 0709203a7..c7bfa7ade 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.kt +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.kt @@ -22,7 +22,7 @@ package com.kunzisoft.keepass.app import android.support.multidex.MultiDexApplication import com.kunzisoft.keepass.compat.PRNGFixes import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.stylish.Stylish +import com.kunzisoft.keepass.activities.stylish.Stylish class App : MultiDexApplication() { diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt index b107d413c..7e248aa4a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutoFillLauncherActivity.kt @@ -30,7 +30,7 @@ import android.support.annotation.RequiresApi import android.support.v7.app.AppCompatActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.fileselect.FileDatabaseSelectActivity +import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java index 99c24ec32..36e241faf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java @@ -44,7 +44,7 @@ import android.widget.Spinner; import android.widget.TextView; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.fileselect.FilePickerStylishActivity; +import com.kunzisoft.keepass.activities.stylish.FilePickerStylishActivity; import com.kunzisoft.keepass.utils.UriUtil; import com.nononsenseapps.filepicker.FilePickerActivity; import com.nononsenseapps.filepicker.Utils; diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java index b7a753ca0..3ded5e21f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java @@ -40,7 +40,7 @@ import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.database.element.PwIconStandard; import com.kunzisoft.keepass.icons.IconPack; import com.kunzisoft.keepass.icons.IconPackChooser; -import com.kunzisoft.keepass.stylish.StylishActivity; +import com.kunzisoft.keepass.activities.stylish.StylishActivity; public class IconPickerDialogFragment extends DialogFragment { diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java deleted file mode 100644 index 3d4ffad56..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.fileselect; - -import android.os.AsyncTask; - -import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; - -class DeleteFileHistoryAsyncTask extends AsyncTask { - - private AfterDeleteFileHistoryListener afterDeleteFileHistoryListener; - private FileDatabaseHistory fileHistory; - private FileDatabaseHistoryAdapter adapter; - - DeleteFileHistoryAsyncTask(AfterDeleteFileHistoryListener afterDeleteFileHistoryListener, FileDatabaseHistory fileHistory, FileDatabaseHistoryAdapter adapter) { - this.afterDeleteFileHistoryListener = afterDeleteFileHistoryListener; - this.fileHistory = fileHistory; - this.adapter = adapter; - } - - protected Void doInBackground(FileDatabaseModel... args) { - fileHistory.deleteDatabaseUri(args[0].getFileUri()); - return null; - } - - protected void onPostExecute(Void v) { - adapter.notifyDataSetChanged(); - if (adapter.getItemCount() == 0) { - if(afterDeleteFileHistoryListener != null) - afterDeleteFileHistoryListener.afterDeleteFile(); - } - } - - public interface AfterDeleteFileHistoryListener { - void afterDeleteFile(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.kt b/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.kt new file mode 100644 index 000000000..107723c2c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/DeleteFileHistoryAsyncTask.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.fileselect + +import android.os.AsyncTask + +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory + +class DeleteFileHistoryAsyncTask(private val afterDeleteFileHistoryListener: (() -> Unit)?, + private val fileHistory: FileDatabaseHistory?, + private val adapter: FileDatabaseHistoryAdapter?) + : AsyncTask() { + + override fun doInBackground(vararg args: FileDatabaseModel): Void? { + fileHistory?.deleteDatabaseUri(args[0].fileUri) + return null + } + + override fun onPostExecute(v: Void) { + adapter?.notifyDataSetChanged() + if (adapter == null || adapter.itemCount == 0) { + afterDeleteFileHistoryListener?.invoke() + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.java deleted file mode 100644 index 4c69b2cbc..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.fileselect; - -import android.content.Context; -import android.content.res.Resources; -import android.net.Uri; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.settings.PreferencesUtil; - -import java.util.List; - -public class FileDatabaseHistoryAdapter extends RecyclerView.Adapter { - - private static final int MENU_CLEAR = 1; - - private Context context; - private LayoutInflater inflater; - private List listFiles; - private FileItemOpenListener fileItemOpenListener; - private FileSelectClearListener fileSelectClearListener; - private FileInformationShowListener fileInformationShowListener; - - private @ColorInt - int defaultColor; - private @ColorInt - int warningColor; - - FileDatabaseHistoryAdapter(Context context, List listFiles) { - this.inflater = LayoutInflater.from(context); - this.context = context; - this.listFiles = listFiles; - - TypedValue typedValue = new TypedValue(); - Resources.Theme theme = context.getTheme(); - theme.resolveAttribute(R.attr.colorAccentCompat, typedValue, true); - warningColor = typedValue.data; - theme.resolveAttribute(android.R.attr.textColorHintInverse, typedValue, true); - defaultColor = typedValue.data; - } - - @NonNull - @Override - public FileDatabaseHistoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View view = inflater.inflate(R.layout.file_row, parent, false); - return new FileDatabaseHistoryViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull FileDatabaseHistoryViewHolder holder, int position) { - FileDatabaseModel fileDatabaseModel = new FileDatabaseModel(context, listFiles.get(position)); - // Context menu creation - holder.getFileContainer().setOnCreateContextMenuListener(new ContextMenuBuilder(fileDatabaseModel)); - // Click item to open file - if (fileItemOpenListener != null) - holder.getFileContainer().setOnClickListener(new FileItemClickListener(position)); - // Assign file name - if (PreferencesUtil.isFullFilePathEnable(context)) - holder.getFileName().setText(Uri.decode(fileDatabaseModel.getFileUri().toString())); - else - holder.getFileName().setText(fileDatabaseModel.getFileName()); - holder.getFileName().setTextSize(PreferencesUtil.getListTextSize(context)); - // Click on information - if (fileInformationShowListener != null) - holder.getFileInformation().setOnClickListener(new FileInformationClickListener(fileDatabaseModel)); - } - - @Override - public int getItemCount() { - return listFiles.size(); - } - - void setOnItemClickListener(FileItemOpenListener fileItemOpenListener) { - this.fileItemOpenListener = fileItemOpenListener; - } - - void setFileSelectClearListener(FileSelectClearListener fileSelectClearListener) { - this.fileSelectClearListener = fileSelectClearListener; - } - - void setFileInformationShowListener(FileInformationShowListener fileInformationShowListener) { - this.fileInformationShowListener = fileInformationShowListener; - } - - public interface FileItemOpenListener { - void onFileItemOpenListener(int itemPosition); - } - - public interface FileSelectClearListener { - boolean onFileSelectClearListener(FileDatabaseModel fileDatabaseModel); - } - - public interface FileInformationShowListener { - void onClickFileInformation(FileDatabaseModel fileDatabaseModel); - } - - private class FileItemClickListener implements View.OnClickListener { - - private int position; - - FileItemClickListener(int position) { - this.position = position; - } - - @Override - public void onClick(View v) { - fileItemOpenListener.onFileItemOpenListener(position); - } - } - - private class FileInformationClickListener implements View.OnClickListener { - - private FileDatabaseModel fileDatabaseModel; - - FileInformationClickListener(FileDatabaseModel fileDatabaseModel) { - this.fileDatabaseModel = fileDatabaseModel; - } - - @Override - public void onClick(View view) { - fileInformationShowListener.onClickFileInformation(fileDatabaseModel); - } - } - - private class ContextMenuBuilder implements View.OnCreateContextMenuListener { - - private FileDatabaseModel fileDatabaseModel; - - ContextMenuBuilder(FileDatabaseModel fileDatabaseModel) { - this.fileDatabaseModel = fileDatabaseModel; - } - - @Override - public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) { - MenuItem clearMenu = contextMenu.add(Menu.NONE, MENU_CLEAR, Menu.NONE, R.string.remove_from_filelist); - clearMenu.setOnMenuItemClickListener(mOnMyActionClickListener); - } - - private MenuItem.OnMenuItemClickListener mOnMyActionClickListener = new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - if (fileSelectClearListener == null) - return false; - switch ( item.getItemId() ) { - case MENU_CLEAR: - return fileSelectClearListener.onFileSelectClearListener(fileDatabaseModel); - default: - return false; - } - } - }; - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt new file mode 100644 index 000000000..c2e89f4f0 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.fileselect + +import android.content.Context +import android.content.res.Resources +import android.net.Uri +import android.support.annotation.ColorInt +import android.support.v7.widget.RecyclerView +import android.util.TypedValue +import android.view.ContextMenu +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.settings.PreferencesUtil + +class FileDatabaseHistoryAdapter(private val context: Context, private val listFiles: List) + : RecyclerView.Adapter() { + + private val inflater: LayoutInflater = LayoutInflater.from(context) + private var fileItemOpenListener: FileItemOpenListener? = null + private var fileSelectClearListener: FileSelectClearListener? = null + private var fileInformationShowListener: FileInformationShowListener? = null + + @ColorInt + private val defaultColor: Int + @ColorInt + private val warningColor: Int + + init { + + val typedValue = TypedValue() + val theme = context.theme + theme.resolveAttribute(R.attr.colorAccentCompat, typedValue, true) + warningColor = typedValue.data + theme.resolveAttribute(android.R.attr.textColorHintInverse, typedValue, true) + defaultColor = typedValue.data + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileDatabaseHistoryViewHolder { + val view = inflater.inflate(R.layout.file_row, parent, false) + return FileDatabaseHistoryViewHolder(view) + } + + override fun onBindViewHolder(holder: FileDatabaseHistoryViewHolder, position: Int) { + val fileDatabaseModel = FileDatabaseModel(context, listFiles[position]) + // Context menu creation + holder.fileContainer.setOnCreateContextMenuListener(ContextMenuBuilder(fileDatabaseModel)) + // Click item to open file + if (fileItemOpenListener != null) + holder.fileContainer.setOnClickListener(FileItemClickListener(position)) + // Assign file name + if (PreferencesUtil.isFullFilePathEnable(context)) + holder.fileName.text = Uri.decode(fileDatabaseModel.fileUri.toString()) + else + holder.fileName.text = fileDatabaseModel.fileName + holder.fileName.textSize = PreferencesUtil.getListTextSize(context) + // Click on information + if (fileInformationShowListener != null) + holder.fileInformation.setOnClickListener(FileInformationClickListener(fileDatabaseModel)) + } + + override fun getItemCount(): Int { + return listFiles.size + } + + fun setOnItemClickListener(fileItemOpenListener: FileItemOpenListener) { + this.fileItemOpenListener = fileItemOpenListener + } + + fun setFileSelectClearListener(fileSelectClearListener: FileSelectClearListener) { + this.fileSelectClearListener = fileSelectClearListener + } + + fun setFileInformationShowListener(fileInformationShowListener: FileInformationShowListener) { + this.fileInformationShowListener = fileInformationShowListener + } + + interface FileItemOpenListener { + fun onFileItemOpenListener(itemPosition: Int) + } + + interface FileSelectClearListener { + fun onFileSelectClearListener(fileDatabaseModel: FileDatabaseModel): Boolean + } + + interface FileInformationShowListener { + fun onClickFileInformation(fileDatabaseModel: FileDatabaseModel) + } + + private inner class FileItemClickListener(private val position: Int) : View.OnClickListener { + + override fun onClick(v: View) { + fileItemOpenListener?.onFileItemOpenListener(position) + } + } + + private inner class FileInformationClickListener(private val fileDatabaseModel: FileDatabaseModel) : View.OnClickListener { + + override fun onClick(view: View) { + fileInformationShowListener?.onClickFileInformation(fileDatabaseModel) + } + } + + private inner class ContextMenuBuilder(private val fileDatabaseModel: FileDatabaseModel) : View.OnCreateContextMenuListener { + + private val mOnMyActionClickListener = MenuItem.OnMenuItemClickListener { item -> + if (fileSelectClearListener == null) + return@OnMenuItemClickListener false + when (item.itemId) { + MENU_CLEAR -> fileSelectClearListener!!.onFileSelectClearListener(fileDatabaseModel) + else -> false + } + } + + override fun onCreateContextMenu(contextMenu: ContextMenu?, view: View?, contextMenuInfo: ContextMenu.ContextMenuInfo?) { + contextMenu?.add(Menu.NONE, MENU_CLEAR, Menu.NONE, R.string.remove_from_filelist) + ?.setOnMenuItemClickListener(mOnMyActionClickListener) + } + } + + inner class FileDatabaseHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var fileContainer: View = itemView.findViewById(R.id.file_container) + var fileName: TextView = itemView.findViewById(R.id.file_filename) + var fileInformation: ImageView = itemView.findViewById(R.id.file_information) + } + + companion object { + + private const val MENU_CLEAR = 1 + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseModel.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseModel.java index 07de0acd8..79c64e1a5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseModel.java +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Jeremy Jamet / Kunzisoft. + * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePass DX. * diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java deleted file mode 100644 index 345a357dd..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseSelectActivity.java +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.fileselect; - -import android.Manifest; -import android.app.Activity; -import android.app.assist.AssistStructure; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.RequiresApi; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.EntrySelectionHelper; -import com.kunzisoft.keepass.activities.GroupActivity; -import com.kunzisoft.keepass.autofill.AutofillHelper; -import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable; -import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable; -import com.kunzisoft.keepass.database.action.ProgressDialogRunnable; -import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment; -import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment; -import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation; -import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; -import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; -import com.kunzisoft.keepass.password.PasswordActivity; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.stylish.StylishActivity; -import com.kunzisoft.keepass.tasks.ActionRunnable; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.utils.UriUtil; - -import net.cachapa.expandablelayout.ExpandableLayout; - -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.net.URLDecoder; - -import permissions.dispatcher.NeedsPermission; -import permissions.dispatcher.OnNeverAskAgain; -import permissions.dispatcher.OnPermissionDenied; -import permissions.dispatcher.OnShowRationale; -import permissions.dispatcher.PermissionRequest; -import permissions.dispatcher.RuntimePermissions; - -@RuntimePermissions -public class FileDatabaseSelectActivity extends StylishActivity implements - CreateFileDialogFragment.DefinePathDialogListener, - AssignMasterKeyDialogFragment.AssignPasswordDialogListener, - FileDatabaseHistoryAdapter.FileItemOpenListener, - FileDatabaseHistoryAdapter.FileSelectClearListener, - FileDatabaseHistoryAdapter.FileInformationShowListener { - - private static final String TAG = "FileDbSelectActivity"; - - private static final String EXTRA_STAY = "EXTRA_STAY"; - - private FileDatabaseHistoryAdapter mAdapter; - private View fileListContainer; - private View createButtonView; - private View browseButtonView; - private View openButtonView; - - private FileDatabaseHistory fileDatabaseHistory; - - private View fileSelectExpandableButton; - private ExpandableLayout fileSelectExpandable; - private EditText openFileNameView; - - private Uri databaseUri; - - private KeyFileHelper keyFileHelper; - - private String defaultPath; - - /* - * ------------------------- - * No Standard Launch, pass by PasswordActivity - * ------------------------- - */ - - /* - * ------------------------- - * Keyboard Launch - * ------------------------- - */ - - public static void launchForKeyboardSelection(Activity activity) { - KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, new Intent(activity, FileDatabaseSelectActivity.class)); - } - - /* - * ------------------------- - * Autofill Launch - * ------------------------- - */ - - @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult(Activity activity, @NonNull AssistStructure assistStructure) { - AutofillHelper.INSTANCE.startActivityForAutofillResult(activity, - new Intent(activity, FileDatabaseSelectActivity.class), - assistStructure); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - fileDatabaseHistory = FileDatabaseHistory.Companion.getInstance(new WeakReference<>(getApplicationContext())); - - setContentView(R.layout.file_selection); - fileListContainer = findViewById(R.id.container_file_list); - - Toolbar toolbar = findViewById(R.id.toolbar); - toolbar.setTitle(""); - setSupportActionBar(toolbar); - - openFileNameView = findViewById(R.id.file_filename); - - // Set the initial value of the filename - defaultPath = Environment.getExternalStorageDirectory().getAbsolutePath() - + getString(R.string.database_file_path_default) - + getString(R.string.database_file_name_default) - + getString(R.string.database_file_extension_default); - openFileNameView.setHint(R.string.open_link_database); - - // Button to expand file selection - fileSelectExpandableButton = findViewById(R.id.file_select_expandable_button); - fileSelectExpandable = findViewById(R.id.file_select_expandable); - fileSelectExpandableButton.setOnClickListener(view -> { - if (fileSelectExpandable.isExpanded()) - fileSelectExpandable.collapse(); - else - fileSelectExpandable.expand(); - }); - - // History list - RecyclerView mListFiles = findViewById(R.id.file_list); - mListFiles.setLayoutManager(new LinearLayoutManager(this)); - - // Open button - openButtonView = findViewById(R.id.open_database); - openButtonView.setOnClickListener(v -> { - String fileName = openFileNameView.getText().toString(); - if (fileName.isEmpty()) - fileName = defaultPath; - launchPasswordActivityWithPath(fileName); - }); - - // Create button - createButtonView = findViewById(R.id.create_database); - createButtonView .setOnClickListener(v -> - FileDatabaseSelectActivityPermissionsDispatcher - .openCreateFileDialogFragmentWithPermissionCheck(FileDatabaseSelectActivity.this) - ); - - keyFileHelper = new KeyFileHelper(this); - browseButtonView = findViewById(R.id.browse_button); - browseButtonView.setOnClickListener(keyFileHelper.getOpenFileOnClickViewListener( - () -> Uri.parse("file://" + openFileNameView.getText().toString()))); - - // Construct adapter with listeners - mAdapter = new FileDatabaseHistoryAdapter(FileDatabaseSelectActivity.this, fileDatabaseHistory.getDatabaseUriList()); - mAdapter.setOnItemClickListener(this); - mAdapter.setFileSelectClearListener(this); - mAdapter.setFileInformationShowListener(this); - mListFiles.setAdapter(mAdapter); - - // Load default database if not an orientation change - if (! (savedInstanceState != null - && savedInstanceState.containsKey(EXTRA_STAY) - && savedInstanceState.getBoolean(EXTRA_STAY, false)) ) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - String fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, ""); - - if (fileName.length() > 0) { - Uri dbUri = UriUtil.parseDefaultFile(fileName); - String scheme = null; - if (dbUri != null) - scheme = dbUri.getScheme(); - - if (!EmptyUtils.INSTANCE.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { - String path = dbUri.getPath(); - File db = new File(path); - - if (db.exists()) { - launchPasswordActivityWithPath(path); - } - } else { - if (dbUri != null) - launchPasswordActivityWithPath(dbUri.toString()); - } - } - } - - new Handler().post(() -> performedNextEducation(new FileDatabaseSelectActivityEducation(this))); - } - - private void performedNextEducation(FileDatabaseSelectActivityEducation fileDatabaseSelectActivityEducation) { - // If no recent files - if (!fileDatabaseHistory.hasRecentFiles() - && fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation( - createButtonView, - tapTargetView -> { - FileDatabaseSelectActivityPermissionsDispatcher - .openCreateFileDialogFragmentWithPermissionCheck(FileDatabaseSelectActivity.this); - return null; - }, - tapTargetView -> { - // But if the user cancel, it can also select a database - performedNextEducation(fileDatabaseSelectActivityEducation); - return null; - }) - ); - else if (fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation( - browseButtonView, - tapTargetView -> { - keyFileHelper.getOpenFileOnClickViewListener().onClick(tapTargetView); - return null; - }, - tapTargetView -> { - fileDatabaseSelectActivityEducation.checkAndPerformedOpenLinkDatabaseEducation( - fileSelectExpandableButton, - tapTargetView1 -> null, - tapTargetView12 -> null - ); - return null; - } - )); - } - - private void fileNoFoundAction(FileNotFoundException e) { - String error = getString(R.string.file_not_found_content); - Toast.makeText(FileDatabaseSelectActivity.this, - error, Toast.LENGTH_LONG).show(); - Log.e(TAG, error, e); - } - - private void launchPasswordActivity(String fileName, String keyFile) { - EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), - () -> { - try { - PasswordActivity.launch(FileDatabaseSelectActivity.this, - fileName, keyFile); - } catch (FileNotFoundException e) { - fileNoFoundAction(e); - } - return null; - }, - () -> { - try { - PasswordActivity.launchForKeyboardResult(FileDatabaseSelectActivity.this, - fileName, keyFile); - finish(); - } catch (FileNotFoundException e) { - fileNoFoundAction(e); - } - return null; - }, - assistStructure -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - try { - PasswordActivity.launchForAutofillResult(FileDatabaseSelectActivity.this, - fileName, keyFile, - assistStructure); - } catch (FileNotFoundException e) { - fileNoFoundAction(e); - } - } - return null; - }); - } - - private void launchPasswordActivityWithPath(String path) { - launchPasswordActivity(path, ""); - // Delete flickering for kitkat <= - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) - overridePendingTransition(0, 0); - } - - private void updateExternalStorageWarning() { - // To show errors - int warning = -1; - String state = Environment.getExternalStorageState(); - if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { - warning = R.string.read_only_warning; - } else if (!state.equals(Environment.MEDIA_MOUNTED)) { - warning = R.string.warning_unmounted; - } - - TextView labelWarningView = findViewById(R.id.label_warning); - if (warning != -1) { - labelWarningView.setText(warning); - labelWarningView.setVisibility(View.VISIBLE); - } else { - labelWarningView.setVisibility(View.INVISIBLE); - } - } - - @Override - protected void onResume() { - super.onResume(); - - updateExternalStorageWarning(); - updateFileListVisibility(); - mAdapter.notifyDataSetChanged(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - // only to keep the current activity - outState.putBoolean(EXTRA_STAY, true); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - // NOTE: delegate the permission handling to generated method - FileDatabaseSelectActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); - } - - @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) - public void openCreateFileDialogFragment() { - CreateFileDialogFragment createFileDialogFragment = new CreateFileDialogFragment(); - createFileDialogFragment.show(getSupportFragmentManager(), "createFileDialogFragment"); - } - - private void updateFileListVisibility() { - if(mAdapter.getItemCount() == 0) - fileListContainer.setVisibility(View.INVISIBLE); - else - fileListContainer.setVisibility(View.VISIBLE); - } - - /** - * Create file for database - * @return If not created, return false - */ - private boolean createDatabaseFile(Uri path) { - - String pathString = URLDecoder.decode(path.getPath()); - // Make sure file name exists - if (pathString.length() == 0) { - Log.e(TAG, getString(R.string.error_filename_required)); - Toast.makeText(FileDatabaseSelectActivity.this, - R.string.error_filename_required, - Toast.LENGTH_LONG).show(); - return false; - } - - // Try to create the file - File file = new File(pathString); - try { - if (file.exists()) { - Log.e(TAG, getString(R.string.error_database_exists) + " " + file); - Toast.makeText(FileDatabaseSelectActivity.this, - R.string.error_database_exists, - Toast.LENGTH_LONG).show(); - return false; - } - File parent = file.getParentFile(); - - if ( parent == null || (parent.exists() && ! parent.isDirectory()) ) { - Log.e(TAG, getString(R.string.error_invalid_path) + " " + file); - Toast.makeText(FileDatabaseSelectActivity.this, - R.string.error_invalid_path, - Toast.LENGTH_LONG).show(); - return false; - } - - if ( ! parent.exists() ) { - // Create parent directory - if ( ! parent.mkdirs() ) { - Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent); - Toast.makeText(FileDatabaseSelectActivity.this, - R.string.error_could_not_create_parent, - Toast.LENGTH_LONG).show(); - return false; - } - } - - return file.createNewFile(); - } catch (IOException e) { - Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.getLocalizedMessage()); - e.printStackTrace(); - Toast.makeText( - FileDatabaseSelectActivity.this, - getText(R.string.error_file_not_create) + " " - + e.getLocalizedMessage(), - Toast.LENGTH_LONG).show(); - return false; - } - } - - @Override - public boolean onDefinePathDialogPositiveClick(Uri pathFile) { - databaseUri = pathFile; - if(createDatabaseFile(pathFile)) { - AssignMasterKeyDialogFragment assignMasterKeyDialogFragment = new AssignMasterKeyDialogFragment(); - assignMasterKeyDialogFragment.show(getSupportFragmentManager(), "passwordDialog"); - return true; - } else - return false; - } - - @Override - public boolean onDefinePathDialogNegativeClick(Uri pathFile) { - return true; - } - - @Override - public void onAssignKeyDialogPositiveClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { - - try { - String databaseFilename = databaseUri.getPath(); - - if (databaseFilename != null) { - // Create the new database and start prof - new Thread(new ProgressDialogRunnable(this, - R.string.progress_create, - progressTaskUpdater -> - new CreateDatabaseRunnable(databaseFilename, database -> { - // TODO store database created - return new AssignPasswordInDatabaseRunnable(FileDatabaseSelectActivity.this, - database, - masterPasswordChecked, - masterPassword, - keyFileChecked, - keyFile, - true, // TODO get readonly - new LaunchGroupActivityFinish(UriUtil.parseDefaultFile(databaseFilename)) - ); - }) - )).start(); - } - } catch (Exception e) { - String error = "Unable to create database with this password and key file"; - Toast.makeText(this, error, Toast.LENGTH_LONG).show(); - Log.e(TAG, error + " " + e.getMessage()); - // TODO remove - e.printStackTrace(); - } - } - - private class LaunchGroupActivityFinish extends ActionRunnable { - - private Uri fileURI; - - LaunchGroupActivityFinish(Uri fileUri) { - super(); - this.fileURI = fileUri; - } - - @Override - public void run() { - finishRun(true, null); - } - - @Override - public void onFinishRun(boolean isSuccess, @Nullable String message) { - runOnUiThread(() -> { - if (isSuccess) { - // Add database to recent files - fileDatabaseHistory.addDatabaseUri(fileURI); - mAdapter.notifyDataSetChanged(); - updateFileListVisibility(); - GroupActivity.Companion.launch(FileDatabaseSelectActivity.this); - } else { - Log.e(TAG, "Unable to open the database"); - } - }); - } - } - - @Override - public void onAssignKeyDialogNegativeClick( - boolean masterPasswordChecked, String masterPassword, - boolean keyFileChecked, Uri keyFile) { - - } - - @Override - public void onFileItemOpenListener(int itemPosition) { - new OpenFileHistoryAsyncTask((fileName, keyFile) -> { - launchPasswordActivity(fileName, keyFile); - updateFileListVisibility(); - }, fileDatabaseHistory).execute(itemPosition); - } - - @Override - public void onClickFileInformation(FileDatabaseModel fileDatabaseModel) { - if (fileDatabaseModel != null) { - FileInformationDialogFragment fileInformationDialogFragment = - FileInformationDialogFragment.newInstance(fileDatabaseModel); - fileInformationDialogFragment.show(getSupportFragmentManager(), "fileInformation"); - } - } - - @Override - public boolean onFileSelectClearListener(final FileDatabaseModel fileDatabaseModel) { - new DeleteFileHistoryAsyncTask(() -> { - fileDatabaseHistory.deleteDatabaseUri(fileDatabaseModel.getFileUri()); - mAdapter.notifyDataSetChanged(); - updateFileListVisibility(); - }, fileDatabaseHistory, mAdapter).execute(fileDatabaseModel); - return true; - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } - - keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, - uri -> { - if (uri != null) { - if (PreferencesUtil.autoOpenSelectedFile(FileDatabaseSelectActivity.this)) { - launchPasswordActivityWithPath(uri.toString()); - } else { - fileSelectExpandable.expand(false); - openFileNameView.setText(uri.toString()); - } - } - }); - } - - @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) - void showRationaleForExternalStorage(final PermissionRequest request) { - new AlertDialog.Builder(this) - .setMessage(R.string.permission_external_storage_rationale_write_database) - .setPositiveButton(R.string.allow, (dialog, which) -> request.proceed()) - .setNegativeButton(R.string.cancel, (dialog, which) -> request.cancel()) - .show(); - } - - @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) - void showDeniedForExternalStorage() { - Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show(); - } - - @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE) - void showNeverAskForExternalStorage() { - Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuUtil.INSTANCE.defaultMenuInflater(getMenuInflater(), menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - return MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item) - && super.onOptionsItemSelected(item); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java b/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java deleted file mode 100644 index 39169eed7..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.fileselect; - -import android.os.AsyncTask; - -import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory; - -class OpenFileHistoryAsyncTask extends AsyncTask { - - private AfterOpenFileHistoryListener afterOpenFileHistoryListener; - private FileDatabaseHistory fileHistory; - private String fileName; - private String keyFile; - - OpenFileHistoryAsyncTask(AfterOpenFileHistoryListener afterOpenFileHistoryListener, FileDatabaseHistory fileHistory) { - this.afterOpenFileHistoryListener = afterOpenFileHistoryListener; - this.fileHistory = fileHistory; - } - - protected Void doInBackground(Integer... args) { - int position = args[0]; - fileName = fileHistory.getDatabaseAt(position); - keyFile = fileHistory.getKeyFileAt(position); - return null; - } - - protected void onPostExecute(Void v) { - afterOpenFileHistoryListener.afterOpenFile(fileName, keyFile); - } - - public interface AfterOpenFileHistoryListener { - void afterOpenFile(String fileName, String keyFile); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryViewHolder.kt b/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.kt similarity index 51% rename from app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryViewHolder.kt rename to app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.kt index 622e1ae01..ef77ae268 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryViewHolder.kt +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/OpenFileHistoryAsyncTask.kt @@ -19,16 +19,26 @@ */ package com.kunzisoft.keepass.fileselect -import android.support.v7.widget.RecyclerView -import android.view.View -import android.widget.ImageView -import android.widget.TextView +import android.os.AsyncTask -import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory -internal class FileDatabaseHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { +class OpenFileHistoryAsyncTask(private val afterOpenFileHistoryListener: ((fileName: String?, keyFile: String?) -> Unit)?, + private val fileHistory: FileDatabaseHistory?) + : AsyncTask() { - var fileContainer: View = itemView.findViewById(R.id.file_container) - var fileName: TextView = itemView.findViewById(R.id.file_filename) - var fileInformation: ImageView = itemView.findViewById(R.id.file_information) + private var fileName: String? = null + private var keyFile: String? = null + + override fun doInBackground(vararg args: Int?): Void? { + args[0]?.let { + fileName = fileHistory?.getDatabaseAt(it) + keyFile = fileHistory?.getKeyFileAt(it) + } + return null + } + + override fun onPostExecute(v: Void?) { + afterOpenFileHistoryListener?.invoke(fileName, keyFile) + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt index b6126e53d..97607ef4d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/database/FileDatabaseHistory.kt @@ -38,6 +38,7 @@ class FileDatabaseHistory private constructor(private val context: WeakReference private val mKeyFilesUriList = ArrayList() private val mPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.get()) + var isEnabled: Boolean = false val databaseUriList: List @@ -170,7 +171,9 @@ class FileDatabaseHistory private constructor(private val context: WeakReference list.clear() for (i in 0 until size) { - list.add(mPreferences.getString(keyPrefix + "_" + i, "")) + mPreferences.getString(keyPrefix + "_" + i, "")?.let { + list.add(it) + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt index 875509217..0a9329d2c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/magikeyboard/KeyboardLauncherActivity.kt @@ -4,7 +4,7 @@ import android.os.Bundle import android.support.v7.app.AppCompatActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.app.App -import com.kunzisoft.keepass.fileselect.FileDatabaseSelectActivity +import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java index 469b2a57c..3f1bb91e7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java +++ b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationCopyingService.java @@ -36,7 +36,7 @@ import android.util.TypedValue; import com.kunzisoft.keepass.R; import com.kunzisoft.keepass.database.exception.SamsungClipboardException; -import com.kunzisoft.keepass.stylish.Stylish; +import com.kunzisoft.keepass.activities.stylish.Stylish; import com.kunzisoft.keepass.timeout.ClipboardHelper; import java.util.ArrayList; diff --git a/app/src/main/java/com/kunzisoft/keepass/password/IntentBuildLauncher.java b/app/src/main/java/com/kunzisoft/keepass/password/IntentBuildLauncher.java deleted file mode 100644 index aa69659d1..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/password/IntentBuildLauncher.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.kunzisoft.keepass.password; - -import android.content.Intent; - -public interface IntentBuildLauncher { - void launchActivity(Intent intent); -} diff --git a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java b/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java deleted file mode 100644 index 5b7a941c3..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/password/PasswordActivity.java +++ /dev/null @@ -1,998 +0,0 @@ -/* - * Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.password; - -import android.Manifest; -import android.app.Activity; -import android.app.assist.AssistStructure; -import android.app.backup.BackupManager; -import android.content.Intent; -import android.content.SharedPreferences; -import android.hardware.fingerprint.FingerprintManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.RequiresApi; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.Toolbar; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.EntrySelectionHelper; -import com.kunzisoft.keepass.activities.GroupActivity; -import com.kunzisoft.keepass.activities.ReadOnlyHelper; -import com.kunzisoft.keepass.activities.lock.LockingActivity; -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.ProgressDialogRunnable; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper; -import com.kunzisoft.keepass.education.PasswordActivityEducation; -import com.kunzisoft.keepass.fileselect.KeyFileHelper; -import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector; -import com.kunzisoft.keepass.fingerprint.FingerPrintExplanationDialog; -import com.kunzisoft.keepass.fingerprint.FingerPrintHelper; -import com.kunzisoft.keepass.magikeyboard.KeyboardHelper; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.stylish.StylishActivity; -import com.kunzisoft.keepass.tasks.ActionRunnable; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.utils.UriUtil; - -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.FileNotFoundException; -import java.lang.ref.WeakReference; - -import permissions.dispatcher.NeedsPermission; -import permissions.dispatcher.OnNeverAskAgain; -import permissions.dispatcher.OnPermissionDenied; -import permissions.dispatcher.OnShowRationale; -import permissions.dispatcher.PermissionRequest; -import permissions.dispatcher.RuntimePermissions; - -@RuntimePermissions -public class PasswordActivity extends StylishActivity - implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback { - - private static final String TAG = PasswordActivity.class.getName(); - - public static final String KEY_DEFAULT_FILENAME = "defaultFileName"; - - private static final String KEY_PASSWORD = "password"; - private static final String KEY_LAUNCH_IMMEDIATELY = "launchImmediately"; - - private Uri mDbUri = null; - SharedPreferences prefs; - SharedPreferences prefsNoBackup; - - private FingerPrintHelper fingerPrintHelper; - private boolean fingerprintMustBeConfigured = true; - private boolean mRememberKeyfile; - - private FingerPrintHelper.Mode fingerPrintMode; - private static final String PREF_KEY_VALUE_PREFIX = "valueFor_"; // key is a combination of db file name and this prefix - private static final String PREF_KEY_IV_PREFIX = "ivFor_"; // key is a combination of db file name and this prefix - - private Toolbar toolbar; - private View fingerprintContainerView; - private FingerPrintAnimatedVector fingerPrintAnimatedVector; - private TextView fingerprintTextView; - private ImageView fingerprintImageView; - private TextView filenameView; - private EditText passwordView; - private EditText keyFileView; - private Button confirmButtonView; - private CompoundButton checkboxPasswordView; - private CompoundButton checkboxKeyfileView; - private CompoundButton checkboxDefaultDatabaseView; - private CompoundButton.OnCheckedChangeListener enableButtonOncheckedChangeListener; - - private boolean readOnly; - - private DefaultCheckChange defaultCheckChange; - private ValidateButtonViewClickListener validateButtonViewClickListener; - - private KeyFileHelper keyFileHelper; - - private static void buildAndLaunchIntent(Activity activity, String fileName, String keyFile, - IntentBuildLauncher intentBuildLauncher) { - Intent intent = new Intent(activity, PasswordActivity.class); - intent.putExtra(UriIntentInitTask.KEY_FILENAME, fileName); - intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile); - intentBuildLauncher.launchActivity(intent); - } - - private static void verifyFileNameUriFromLaunch(String fileName) throws FileNotFoundException { - if (EmptyUtils.INSTANCE.isNullOrEmpty(fileName)) { - throw new FileNotFoundException(); - } - - Uri uri = UriUtil.parseDefaultFile(fileName); - assert uri != null; - String scheme = uri.getScheme(); - - if (!EmptyUtils.INSTANCE.isNullOrEmpty(scheme) && scheme.equalsIgnoreCase("file")) { - File dbFile = new File(uri.getPath()); - if (!dbFile.exists()) { - throw new FileNotFoundException(); - } - } - } - - /* - * ------------------------- - * Standard Launch - * ------------------------- - */ - - public static void launch( - Activity activity, - String fileName, - String keyFile) throws FileNotFoundException { - verifyFileNameUriFromLaunch(fileName); - buildAndLaunchIntent(activity, fileName, keyFile, activity::startActivity); - } - - /* - * ------------------------- - * Keyboard Launch - * ------------------------- - */ - - public static void launchForKeyboardResult( - Activity activity, - String fileName, - String keyFile) throws FileNotFoundException { - verifyFileNameUriFromLaunch(fileName); - - buildAndLaunchIntent(activity, fileName, keyFile, (intent) -> { - KeyboardHelper.INSTANCE.startActivityForKeyboardSelection(activity, intent); - }); - } - - /* - * ------------------------- - * Autofill Launch - * ------------------------- - */ - - @RequiresApi(api = Build.VERSION_CODES.O) - public static void launchForAutofillResult( - Activity activity, - String fileName, - String keyFile, - AssistStructure assistStructure) throws FileNotFoundException { - verifyFileNameUriFromLaunch(fileName); - - if ( assistStructure != null ) { - buildAndLaunchIntent(activity, fileName, keyFile, (intent) -> { - AutofillHelper.INSTANCE.startActivityForAutofillResult( - activity, - intent, - assistStructure); - }); - } else { - launch(activity, fileName, keyFile); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - prefs = PreferenceManager.getDefaultSharedPreferences(this); - prefsNoBackup = PreferencesUtil.getNoBackupSharedPreferences(getApplicationContext()); - - mRememberKeyfile = prefs.getBoolean(getString(R.string.keyfile_key), - getResources().getBoolean(R.bool.keyfile_default)); - - setContentView(R.layout.password); - - toolbar = findViewById(R.id.toolbar); - toolbar.setTitle(getString(R.string.app_name)); - setSupportActionBar(toolbar); - assert getSupportActionBar() != null; - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - - confirmButtonView = findViewById(R.id.pass_ok); - filenameView = findViewById(R.id.filename); - passwordView = findViewById(R.id.password); - keyFileView = findViewById(R.id.pass_keyfile); - checkboxPasswordView = findViewById(R.id.password_checkbox); - checkboxKeyfileView = findViewById(R.id.keyfile_checkox); - checkboxDefaultDatabaseView = findViewById(R.id.default_database); - - readOnly = ReadOnlyHelper.INSTANCE.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState); - - View browseView = findViewById(R.id.browse_button); - keyFileHelper = new KeyFileHelper(PasswordActivity.this); - browseView.setOnClickListener(keyFileHelper.getOpenFileOnClickViewListener()); - - passwordView.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void afterTextChanged(Editable editable) { - if (!editable.toString().isEmpty() && !checkboxPasswordView.isChecked()) - checkboxPasswordView.setChecked(true); - } - }); - keyFileView.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void afterTextChanged(Editable editable) { - if (!editable.toString().isEmpty() && !checkboxKeyfileView.isChecked()) - checkboxKeyfileView.setChecked(true); - } - }); - - defaultCheckChange = new DefaultCheckChange(); - validateButtonViewClickListener = new ValidateButtonViewClickListener(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - fingerprintContainerView = findViewById(R.id.fingerprint_container); - fingerprintTextView = findViewById(R.id.fingerprint_label); - fingerprintImageView = findViewById(R.id.fingerprint_image); - initForFingerprint(); - // Init the fingerprint animation - fingerPrintAnimatedVector = new FingerPrintAnimatedVector(this, - fingerprintImageView); - } - } - - @Override - protected void onResume() { - // If the database isn't accessible make sure to clear the password field, if it - // was saved in the instance state - if (App.Companion.getCurrentDatabase().getLoaded()) { - setEmptyViews(); - } - - // For check shutdown - super.onResume(); - - // Enable or not the open button - if (!PreferencesUtil.emptyPasswordAllowed(PasswordActivity.this)) { - confirmButtonView.setEnabled(checkboxPasswordView.isChecked()); - } else { - confirmButtonView.setEnabled(true); - } - enableButtonOncheckedChangeListener = (buttonView, isChecked) -> { - if (!PreferencesUtil.emptyPasswordAllowed(PasswordActivity.this)) { - confirmButtonView.setEnabled(isChecked); - } - }; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // Check if fingerprint well init (be called the first time the fingerprint is configured - // and the activity still active) - if (fingerPrintHelper == null || !fingerPrintHelper.isFingerprintInitialized()) { - initForFingerprint(); - } - - // Start the animation in all cases - if (fingerPrintAnimatedVector != null) { - fingerPrintAnimatedVector.startScan(); - } - } else { - checkboxPasswordView.setOnCheckedChangeListener(enableButtonOncheckedChangeListener); - } - - new UriIntentInitTask(new WeakReference<>(this), this, mRememberKeyfile) - .execute(getIntent()); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - ReadOnlyHelper.INSTANCE.onSaveInstanceState(outState, readOnly); - super.onSaveInstanceState(outState); - } - - @Override - public void onPostInitTask(Uri dbUri, Uri keyFileUri, Integer errorStringId) { - mDbUri = dbUri; - - if (errorStringId != null) { - Toast.makeText(PasswordActivity.this, errorStringId, Toast.LENGTH_LONG).show(); - finish(); - return; - } - - // Verify permission to read file - if (mDbUri != null - && !dbUri.getScheme().contains("content")) - PasswordActivityPermissionsDispatcher - .doNothingWithPermissionCheck(this); - - // Define title - String dbUriString = (mDbUri == null) ? "" : mDbUri.toString(); - if (!dbUriString.isEmpty()) { - if (PreferencesUtil.isFullFilePathEnable(this)) - filenameView.setText(dbUriString); - else - filenameView.setText(new File(mDbUri.getPath()).getName()); // TODO Encapsulate - } - - // Define Key File text - String keyUriString = (keyFileUri == null) ? "" : keyFileUri.toString(); - if (!keyUriString.isEmpty() && mRememberKeyfile) { // Bug KeepassDX #18 - populateKeyFileTextView(keyUriString); - } - - // Define listeners for default database checkbox and validate button - checkboxDefaultDatabaseView.setOnCheckedChangeListener(defaultCheckChange); - confirmButtonView.setOnClickListener(validateButtonViewClickListener); - - // Retrieve settings for default database - String defaultFilename = prefs.getString(KEY_DEFAULT_FILENAME, ""); - if (mDbUri!=null - && !EmptyUtils.INSTANCE.isNullOrEmpty(mDbUri.getPath()) - && UriUtil.equalsDefaultfile(mDbUri, defaultFilename)) { - checkboxDefaultDatabaseView.setChecked(true); - } - - // checks if fingerprint is available, will also start listening for fingerprints when available - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - checkFingerprintAvailability(); - } - - // If Activity is launch with a password and want to open directly - Intent intent = getIntent(); - String password = intent.getStringExtra(KEY_PASSWORD); - boolean launch_immediately = intent.getBooleanExtra(KEY_LAUNCH_IMMEDIATELY, false); - if (password != null) { - populatePasswordTextView(password); - } - if (launch_immediately) { - verifyCheckboxesAndLoadDatabase(password, keyFileUri); - } - } - - private void setEmptyViews() { - populatePasswordTextView(null); - // Bug KeepassDX #18 - if (!mRememberKeyfile) { - populateKeyFileTextView(null); - } - } - - private void populatePasswordTextView(String text) { - if (text == null || text.isEmpty()) { - passwordView.setText(""); - if (checkboxPasswordView.isChecked()) - checkboxPasswordView.setChecked(false); - } else { - passwordView.setText(text); - if (!checkboxPasswordView.isChecked()) - checkboxPasswordView.setChecked(true); - } - } - - private void populateKeyFileTextView(String text) { - if (text == null || text.isEmpty()) { - keyFileView.setText(""); - if (checkboxKeyfileView.isChecked()) - checkboxKeyfileView.setChecked(false); - } else { - keyFileView.setText(text); - if (!checkboxKeyfileView.isChecked()) - checkboxKeyfileView.setChecked(true); - } - } - - // fingerprint related code here - @RequiresApi(api = Build.VERSION_CODES.M) - private void initForFingerprint() { - fingerPrintMode = FingerPrintHelper.Mode.NOT_CONFIGURED_MODE; - - fingerPrintHelper = new FingerPrintHelper(this, this); - - checkboxPasswordView.setOnCheckedChangeListener((compoundButton, checked) -> { - if ( !fingerprintMustBeConfigured ) { - // encrypt or decrypt mode based on how much input or not - if (checked) { - toggleFingerprintMode(FingerPrintHelper.Mode.STORE_MODE); - } else { - if (!prefsNoBackup.contains(getPreferenceKeyValue())) { - // This happens when no fingerprints are registered. - toggleFingerprintMode(FingerPrintHelper.Mode.WAITING_PASSWORD_MODE); - } else { - toggleFingerprintMode(FingerPrintHelper.Mode.OPEN_MODE); - } - } - } - - // Add old listener to enable the button, only be call here because of onCheckedChange bug - if (enableButtonOncheckedChangeListener != null) - enableButtonOncheckedChangeListener.onCheckedChanged(compoundButton, checked); - }); - - // callback for fingerprint findings - fingerPrintHelper.setAuthenticationCallback(new FingerprintManager.AuthenticationCallback() { - @Override - public void onAuthenticationError( - final int errorCode, - final CharSequence errString) { - switch (errorCode) { - case 5: - Log.i(TAG, "Fingerprint authentication error. Code : " + errorCode + " Error : " + errString); - break; - default: - Log.e(TAG, "Fingerprint authentication error. Code : " + errorCode + " Error : " + errString); - setFingerPrintView(errString.toString(), true); - } - } - - @Override - public void onAuthenticationHelp( - final int helpCode, - final CharSequence helpString) { - Log.w(TAG, "Fingerprint authentication help. Code : " + helpCode + " Help : " + helpString); - showError(helpString); - setFingerPrintView(helpString.toString(), true); - fingerprintTextView.setText(helpString); - } - - @Override - public void onAuthenticationFailed() { - Log.e(TAG, "Fingerprint authentication failed, fingerprint not recognized"); - showError(R.string.fingerprint_not_recognized); - } - - @Override - public void onAuthenticationSucceeded(final FingerprintManager.AuthenticationResult result) { - switch (fingerPrintMode) { - case STORE_MODE: - // newly store the entered password in encrypted way - final String password = passwordView.getText().toString(); - fingerPrintHelper.encryptData(password); - break; - case OPEN_MODE: - // retrieve the encrypted value from preferences - final String encryptedValue = prefsNoBackup.getString(getPreferenceKeyValue(), null); - if (encryptedValue != null) { - fingerPrintHelper.decryptData(encryptedValue); - } - break; - } - } - }); - } - - private String getPreferenceKeyValue() { - // makes it possible to store passwords uniqly per database - return PREF_KEY_VALUE_PREFIX + (mDbUri != null ? mDbUri.getPath() : ""); - } - - private String getPreferenceKeyIvSpec() { - return PREF_KEY_IV_PREFIX + (mDbUri != null ? mDbUri.getPath() : ""); - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private void initEncryptData() { - setFingerPrintView(R.string.store_with_fingerprint); - fingerPrintMode = FingerPrintHelper.Mode.STORE_MODE; - if (fingerPrintHelper != null) - fingerPrintHelper.initEncryptData(); - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private void initDecryptData() { - setFingerPrintView(R.string.scanning_fingerprint); - fingerPrintMode = FingerPrintHelper.Mode.OPEN_MODE; - if (fingerPrintHelper != null) { - final String ivSpecValue = prefsNoBackup.getString(getPreferenceKeyIvSpec(), null); - if (ivSpecValue != null) - fingerPrintHelper.initDecryptData(ivSpecValue); - } - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private void initWaitData() { - setFingerPrintView(R.string.no_password_stored, true); - fingerPrintMode = FingerPrintHelper.Mode.WAITING_PASSWORD_MODE; - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private synchronized void toggleFingerprintMode(final FingerPrintHelper.Mode newMode) { - switch (newMode) { - case WAITING_PASSWORD_MODE: - setFingerPrintView(R.string.no_password_stored, true); - break; - case STORE_MODE: - setFingerPrintView(R.string.store_with_fingerprint); - break; - case OPEN_MODE: - setFingerPrintView(R.string.scanning_fingerprint); - break; - } - if( !newMode.equals(fingerPrintMode) ) { - fingerPrintMode = newMode; - reInitWithFingerprintMode(); - } - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private synchronized void reInitWithFingerprintMode() { - switch (fingerPrintMode) { - case STORE_MODE: - initEncryptData(); - break; - case WAITING_PASSWORD_MODE: - initWaitData(); - break; - case OPEN_MODE: - initDecryptData(); - break; - } - // Show fingerprint key deletion - invalidateOptionsMenu(); - } - - @Override - protected void onPause() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (fingerPrintAnimatedVector != null) { - fingerPrintAnimatedVector.stopScan(); - } - // stop listening when we go in background - fingerPrintMode = FingerPrintHelper.Mode.NOT_CONFIGURED_MODE; - if (fingerPrintHelper != null) { - fingerPrintHelper.stopListening(); - } - } - super.onPause(); - } - - private void setFingerPrintVisibility(final int vis) { - runOnUiThread(() -> fingerprintContainerView.setVisibility(vis)); - } - - private void setFingerPrintView(final int textId) { - setFingerPrintView(textId, false); - } - - private void setFingerPrintView(final int textId, boolean lock) { - setFingerPrintView(getString(textId), lock); - } - - private void setFingerPrintView(final CharSequence text, boolean lock) { - runOnUiThread(() -> { - if (lock) { - fingerprintContainerView.setAlpha(0.8f); - } else - fingerprintContainerView.setAlpha(1f); - fingerprintTextView.setText(text); - }); - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private synchronized void checkFingerprintAvailability() { - // fingerprint not supported (by API level or hardware) so keep option hidden - // or manually disable - if (!PreferencesUtil .isFingerprintEnable(getApplicationContext()) - || !FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager.class))) { - setFingerPrintVisibility(View.GONE); - } - // fingerprint is available but not configured show icon but in disabled state with some information - else { - // show explanations - fingerprintContainerView.setOnClickListener(view -> { - FingerPrintExplanationDialog fingerPrintDialog = new FingerPrintExplanationDialog(); - fingerPrintDialog.show(getSupportFragmentManager(), "fingerprintDialog"); - }); - setFingerPrintVisibility(View.VISIBLE); - - if (!fingerPrintHelper.hasEnrolledFingerprints()) { - // This happens when no fingerprints are registered. Listening won't start - setFingerPrintView(R.string.configure_fingerprint, true); - } - // finally fingerprint available and configured so we can use it - else { - fingerprintMustBeConfigured = false; - - // fingerprint available but no stored password found yet for this DB so show info don't listen - if (!prefsNoBackup.contains(getPreferenceKeyValue())) { - if (checkboxPasswordView.isChecked()) { - // listen for encryption - initEncryptData(); - } else { - // wait for typing - initWaitData(); - } - } - // all is set here so we can confirm to user and start listening for fingerprints - else { - // listen for decryption - initDecryptData(); - } - } - } - - // Show fingerprint key deletion - invalidateOptionsMenu(); - } - - private void removePrefsNoBackupKey() { - prefsNoBackup.edit() - .remove(getPreferenceKeyValue()) - .remove(getPreferenceKeyIvSpec()) - .apply(); - } - - @Override - public void handleEncryptedResult( - final String value, - final String ivSpec) { - prefsNoBackup.edit() - .putString(getPreferenceKeyValue(), value) - .putString(getPreferenceKeyIvSpec(), ivSpec) - .apply(); - verifyAllViewsAndLoadDatabase(); - setFingerPrintView(R.string.encrypted_value_stored); - } - - @Override - public void handleDecryptedResult(final String passwordValue) { - // Load database directly - verifyKeyFileViewsAndLoadDatabase(passwordValue); - } - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - public void onInvalidKeyException(Exception e) { - showError(getString(R.string.fingerprint_invalid_key)); - deleteEntryKey(); - } - - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - public void onFingerPrintException(Exception e) { - // Don't show error here; - // showError(getString(R.string.fingerprint_error, e.getMessage())); - // Can be uninit in Activity and init in fragment - setFingerPrintView(e.getLocalizedMessage(), true); - } - - @RequiresApi(api = Build.VERSION_CODES.M) - private void deleteEntryKey() { - fingerPrintHelper.deleteEntryKey(); - removePrefsNoBackupKey(); - fingerPrintMode = FingerPrintHelper.Mode.NOT_CONFIGURED_MODE; - checkFingerprintAvailability(); - } - - private void showError(final int messageId) { - showError(getString(messageId)); - } - - private void showError(final CharSequence message) { - runOnUiThread(() -> Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show()); - } - - private class DefaultCheckChange implements CompoundButton.OnCheckedChangeListener { - @Override - public void onCheckedChanged( - CompoundButton buttonView, - boolean isChecked) { - - String newDefaultFileName = ""; - if (isChecked) { - newDefaultFileName = mDbUri.toString(); - } - - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(KEY_DEFAULT_FILENAME, newDefaultFileName); - editor.apply(); - - BackupManager backupManager = new BackupManager(PasswordActivity.this); - backupManager.dataChanged(); - } - } - - private class ValidateButtonViewClickListener implements View.OnClickListener { - public void onClick(View view) { - verifyAllViewsAndLoadDatabase(); - } - } - - private void verifyAllViewsAndLoadDatabase() { - String pass = passwordView.getText().toString(); - String keyfile = keyFileView.getText().toString(); - verifyCheckboxesAndLoadDatabase(pass, UriUtil.parseDefaultFile(keyfile)); - } - - private void verifyCheckboxesAndLoadDatabase(String pass, Uri keyfile) { - if (!checkboxPasswordView.isChecked()) { - pass = null; - } - if (!checkboxKeyfileView.isChecked()) { - keyfile = null; - } - loadDatabase(pass, keyfile); - } - - private void verifyKeyFileViewsAndLoadDatabase(String password) { - String key = keyFileView.getText().toString(); - Uri keyUri = UriUtil.parseDefaultFile(key); - if (!checkboxKeyfileView.isChecked()) { - keyUri = null; - } - loadDatabase(password, keyUri); - } - - private void loadDatabase(String password, Uri keyfile) { - // Clear before we load - Database database = App.Companion.getCurrentDatabase(); - database.closeAndClear(getApplicationContext()); - - // Show the progress dialog and load the database - new Thread(new ProgressDialogRunnable( - this, - R.string.loading_database, - progressTaskUpdater -> new LoadDatabaseRunnable( - new WeakReference<>(PasswordActivity.this), - database, - mDbUri, - password, - keyfile, - progressTaskUpdater, - new AfterLoadingDatabase(database)) - )).start(); - } - - /** - * Called after verify and try to opening the database - */ - private final class AfterLoadingDatabase extends ActionRunnable { - - protected Database database; - - AfterLoadingDatabase(Database database) { - super(); - this.database = database; - } - - @Override - public void onFinishRun(boolean isSuccess, @Nullable String message) { - runOnUiThread(() -> { - // Recheck fingerprint if error - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // Stay with the same mode - reInitWithFingerprintMode(); - } - - if (database.isPasswordEncodingError()) { - PasswordEncodingDialogHelper dialog = new PasswordEncodingDialogHelper(); - dialog.show(PasswordActivity.this, (dialog1, which) -> launchGroupActivity()); - } else if (isSuccess) { - launchGroupActivity(); - } else { - if ( getMessage() != null && getMessage().length() > 0 ) { - Toast.makeText(PasswordActivity.this, getMessage(), Toast.LENGTH_LONG).show(); - } - } - }); - } - } - - private void launchGroupActivity() { - EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), - () -> { - GroupActivity.Companion.launch(PasswordActivity.this, readOnly); - return null; - }, - () -> { - GroupActivity.Companion.launchForKeyboardSelection(PasswordActivity.this, readOnly); - // Do not keep history - finish(); - return null; - }, - assistStructure -> { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - GroupActivity.Companion.launchForAutofillResult(PasswordActivity.this, assistStructure, readOnly); - } - return null; - }); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - // Read menu - inflater.inflate(R.menu.open_file, menu); - changeOpenFileReadIcon(menu.findItem(R.id.menu_open_file_read_mode_key)); - - MenuUtil.INSTANCE.defaultMenuInflater(inflater, menu); - - // Fingerprint menu - if (!fingerprintMustBeConfigured - && prefsNoBackup.contains(getPreferenceKeyValue()) ) - inflater.inflate(R.menu.fingerprint, menu); - - super.onCreateOptionsMenu(menu); - - // Show education views - new Handler().post(() -> performedNextEducation(new PasswordActivityEducation(this), menu)); - - return true; - } - - private void performedNextEducation(PasswordActivityEducation passwordActivityEducation, - Menu menu) { - if (passwordActivityEducation.checkAndPerformedFingerprintUnlockEducation( - toolbar, - tapTargetView -> { - performedNextEducation(passwordActivityEducation, menu); - return null; - }, - tapTargetView -> { - performedNextEducation(passwordActivityEducation, menu); - return null; - }) - ); - else if (toolbar.findViewById(R.id.menu_open_file_read_mode_key) != null - && passwordActivityEducation.checkAndPerformedReadOnlyEducation( - toolbar.findViewById(R.id.menu_open_file_read_mode_key), - tapTargetView -> { - onOptionsItemSelected(menu.findItem(R.id.menu_open_file_read_mode_key)); - performedNextEducation(passwordActivityEducation, menu); - return null; - }, - tapTargetView -> { - performedNextEducation(passwordActivityEducation, menu); - return null; - }) - ); - else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M - && PreferencesUtil.isFingerprintEnable(getApplicationContext()) - && FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager.class)) - && passwordActivityEducation.checkAndPerformedFingerprintEducation(fingerprintImageView, - tapTargetView -> null, - tapTargetView -> null) - ); - } - - private void changeOpenFileReadIcon(MenuItem togglePassword) { - if ( readOnly ) { - togglePassword.setTitle(R.string.menu_file_selection_read_only); - togglePassword.setIcon(R.drawable.ic_read_only_white_24dp); - } else { - togglePassword.setTitle(R.string.menu_open_file_read_and_write); - togglePassword.setIcon(R.drawable.ic_read_write_white_24dp); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case android.R.id.home: - finish(); - break; - case R.id.menu_open_file_read_mode_key: - readOnly = !readOnly; - changeOpenFileReadIcon(item); - break; - case R.id.menu_fingerprint_remove_key: - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - deleteEntryKey(); - } - break; - default: - return MenuUtil.INSTANCE.onDefaultMenuOptionsItemSelected(this, item); - } - - return super.onOptionsItemSelected(item); - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - // NOTE: delegate the permission handling to generated method - PasswordActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); - } - - @Override - protected void onActivityResult( - int requestCode, - int resultCode, - Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - // To get entry in result - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - AutofillHelper.INSTANCE.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data); - } - - boolean keyFileResult = false; - if (keyFileHelper != null) { - keyFileResult = keyFileHelper.onActivityResultCallback(requestCode, resultCode, data, - uri -> { - if (uri != null) { - populateKeyFileTextView(uri.toString()); - } - }); - } - if (!keyFileResult) { - // this block if not a key file response - switch (resultCode) { - case LockingActivity.RESULT_EXIT_LOCK: - case Activity.RESULT_CANCELED: - setEmptyViews(); - App.Companion.getCurrentDatabase().closeAndClear(getApplicationContext()); - break; - } - } - } - - @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) - public void doNothing() {} - - @OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE) - void showRationaleForExternalStorage(final PermissionRequest request) { - new AlertDialog.Builder(this) - .setMessage(R.string.permission_external_storage_rationale_read_database) - .setPositiveButton(R.string.allow, (dialog, which) -> request.proceed()) - .setNegativeButton(R.string.cancel, (dialog, which) -> request.cancel()) - .show(); - } - - @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) - void showDeniedForExternalStorage() { - Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show(); - finish(); - } - - @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE) - void showNeverAskForExternalStorage() { - Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show(); - finish(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java index fadc84980..306d10771 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.java @@ -24,7 +24,7 @@ import android.support.v7.widget.Toolbar; import android.view.MenuItem; import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.stylish.StylishActivity; +import com.kunzisoft.keepass.activities.stylish.StylishActivity; public class MagikIMESettings extends StylishActivity { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java index 768370de8..908b98047 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.java @@ -61,7 +61,7 @@ import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseNamePrefe import com.kunzisoft.keepass.settings.preferencedialogfragment.MemoryUsagePreferenceDialogFragmentCompat; import com.kunzisoft.keepass.settings.preferencedialogfragment.ParallelismPreferenceDialogFragmentCompat; import com.kunzisoft.keepass.settings.preferencedialogfragment.RoundsPreferenceDialogFragmentCompat; -import com.kunzisoft.keepass.stylish.Stylish; +import com.kunzisoft.keepass.activities.stylish.Stylish; import java.lang.ref.WeakReference; diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt index 854602b4e..ec4779cfb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/MenuUtil.kt @@ -30,7 +30,7 @@ import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.AboutActivity import com.kunzisoft.keepass.activities.ReadOnlyHelper.READ_ONLY_DEFAULT import com.kunzisoft.keepass.settings.SettingsActivity -import com.kunzisoft.keepass.stylish.StylishActivity +import com.kunzisoft.keepass.activities.stylish.StylishActivity object MenuUtil { From a158e96ba6d27cd2a6b390f144e10d8b4f8cb6a5 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 10 Jul 2019 17:59:59 +0200 Subject: [PATCH 143/289] Refactor BasicViewHolder --- .../keepass/adapters/BasicViewHolder.kt | 33 ------------------ .../keepass/adapters/EntryViewHolder.kt | 34 ------------------- .../keepass/adapters/GroupViewHolder.kt | 34 ------------------- .../kunzisoft/keepass/adapters/NodeAdapter.kt | 33 +++++++++++++++++- 4 files changed, 32 insertions(+), 102 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.kt deleted file mode 100644 index 4d5ab9169..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/BasicViewHolder.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.adapters - -import android.support.v7.widget.RecyclerView -import android.view.View -import android.widget.ImageView -import android.widget.TextView - -abstract class BasicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - - var container: View? = null - var icon: ImageView? = null - var text: TextView? = null - var subText: TextView? = null -} diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.kt deleted file mode 100644 index 7b1ffecc1..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/EntryViewHolder.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.adapters - -import android.view.View - -import com.kunzisoft.keepass.R - -internal class EntryViewHolder(itemView: View) : BasicViewHolder(itemView) { - - init { - container = itemView.findViewById(R.id.entry_container) - icon = itemView.findViewById(R.id.entry_icon) - text = itemView.findViewById(R.id.entry_text) - subText = itemView.findViewById(R.id.entry_subtext) - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.kt deleted file mode 100644 index b73ca7d91..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/GroupViewHolder.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2019 Jeremy Jamet / Kunzisoft. - * - * This file is part of KeePass DX. - * - * KeePass DX is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * KeePass DX is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with KeePass DX. If not, see . - * - */ -package com.kunzisoft.keepass.adapters - -import android.view.View - -import com.kunzisoft.keepass.R - -internal class GroupViewHolder(itemView: View) : BasicViewHolder(itemView) { - - init { - container = itemView.findViewById(R.id.group_container) - icon = itemView.findViewById(R.id.group_icon) - text = itemView.findViewById(R.id.group_text) - subText = itemView.findViewById(R.id.group_subtext) - } -} 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 eb5de13ef..8e497e182 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodeAdapter.kt @@ -26,6 +26,8 @@ import android.support.v7.widget.RecyclerView import android.support.v7.widget.util.SortedListAdapterCallback import android.util.Log import android.view.* +import android.widget.ImageView +import android.widget.TextView import android.widget.Toast import com.kunzisoft.keepass.R import com.kunzisoft.keepass.app.App @@ -40,7 +42,7 @@ class NodeAdapter * @param context Context to use */ (private val context: Context, private val menuInflater: MenuInflater) - : RecyclerView.Adapter() { + : RecyclerView.Adapter() { private val nodeSortedList: SortedList private val inflater: LayoutInflater = LayoutInflater.from(context) @@ -353,6 +355,35 @@ class NodeAdapter } } + abstract class BasicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var container: View? = null + var icon: ImageView? = null + var text: TextView? = null + var subText: TextView? = null + } + + + internal class GroupViewHolder(itemView: View) : BasicViewHolder(itemView) { + + init { + container = itemView.findViewById(R.id.group_container) + icon = itemView.findViewById(R.id.group_icon) + text = itemView.findViewById(R.id.group_text) + subText = itemView.findViewById(R.id.group_subtext) + } + } + + internal class EntryViewHolder(itemView: View) : BasicViewHolder(itemView) { + + init { + container = itemView.findViewById(R.id.entry_container) + icon = itemView.findViewById(R.id.entry_icon) + text = itemView.findViewById(R.id.entry_text) + subText = itemView.findViewById(R.id.entry_subtext) + } + } + companion object { private val TAG = NodeAdapter::class.java.name } From 6a5ed7f460c45b680badf52dd261d1b7abc1e625 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 10 Jul 2019 19:10:59 +0200 Subject: [PATCH 144/289] Fix kotlin null crash --- .../activities/FileDatabaseSelectActivity.kt | 2 +- .../keepass/activities/PasswordActivity.kt | 2 +- .../dialogs/AssignMasterKeyDialogFragment.kt | 117 ++++++++++-------- .../dialogs/CreateFileDialogFragment.java | 2 +- .../fileselect/FileDatabaseHistoryAdapter.kt | 9 +- 5 files changed, 67 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 100f7ba71..1601201e6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -471,7 +471,7 @@ class FileDatabaseSelectActivity : StylishActivity(), return true } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 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 bfede2351..98f1dfc42 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -743,7 +743,7 @@ class PasswordActivity : StylishActivity(), override fun onActivityResult( requestCode: Int, resultCode: Int, - data: Intent) { + data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // To get entry in result diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt index 8334b0ca8..490eab2d6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt @@ -40,19 +40,19 @@ import com.kunzisoft.keepass.utils.UriUtil class AssignMasterKeyDialogFragment : DialogFragment() { - private var masterPassword: String? = null - private var mKeyfile: Uri? = null + private var mMasterPassword: String? = null + private var mKeyFile: Uri? = null private var rootView: View? = null private var passwordCheckBox: CompoundButton? = null private var passView: TextView? = null private var passConfView: TextView? = null - private var keyfileCheckBox: CompoundButton? = null - private var keyfileView: TextView? = null + private var keyFileCheckBox: CompoundButton? = null + private var keyFileView: TextView? = null private var mListener: AssignPasswordDialogListener? = null - private var keyFileHelper: KeyFileHelper? = null + private var mKeyFileHelper: KeyFileHelper? = null interface AssignPasswordDialogListener { fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?, @@ -67,7 +67,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() { try { mListener = activity as AssignPasswordDialogListener? } catch (e: ClassCastException) { - throw ClassCastException(activity!!.toString() + throw ClassCastException(activity?.toString() + " must implement " + AssignPasswordDialogListener::class.java.name) } @@ -99,51 +99,51 @@ class AssignMasterKeyDialogFragment : DialogFragment() { }) passConfView = rootView?.findViewById(R.id.pass_conf_password) - keyfileCheckBox = rootView?.findViewById(R.id.keyfile_checkox) - keyfileView = rootView?.findViewById(R.id.pass_keyfile) - keyfileView?.addTextChangedListener(object : TextWatcher { + keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox) + keyFileView = rootView?.findViewById(R.id.pass_keyfile) + keyFileView?.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun afterTextChanged(editable: Editable) { - keyfileCheckBox?.isChecked = true + keyFileCheckBox?.isChecked = true } }) - keyFileHelper = KeyFileHelper(this) + mKeyFileHelper = KeyFileHelper(this) rootView?.findViewById(R.id.browse_button)?.setOnClickListener { view -> - keyFileHelper?.openFileOnClickViewListener?.onClick(view) } + mKeyFileHelper?.openFileOnClickViewListener?.onClick(view) } val dialog = builder.create() - if (passwordCheckBox != null && keyfileCheckBox!= null) { + if (passwordCheckBox != null && keyFileCheckBox!= null) { dialog.setOnShowListener { dialog1 -> val positiveButton = (dialog1 as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) positiveButton.setOnClickListener { - masterPassword = "" - mKeyfile = null + mMasterPassword = "" + mKeyFile = null var error = verifyPassword() || verifyFile() - if (!passwordCheckBox!!.isChecked && !keyfileCheckBox!!.isChecked) { + if (!passwordCheckBox!!.isChecked && !keyFileCheckBox!!.isChecked) { error = true showNoKeyConfirmationDialog() } if (!error) { mListener!!.onAssignKeyDialogPositiveClick( - passwordCheckBox!!.isChecked, masterPassword, - keyfileCheckBox!!.isChecked, mKeyfile) + passwordCheckBox!!.isChecked, mMasterPassword, + keyFileCheckBox!!.isChecked, mKeyFile) dismiss() } } val negativeButton = dialog1.getButton(DialogInterface.BUTTON_NEGATIVE) negativeButton.setOnClickListener { - mListener!!.onAssignKeyDialogNegativeClick( - passwordCheckBox!!.isChecked, masterPassword, - keyfileCheckBox!!.isChecked, mKeyfile) + mListener?.onAssignKeyDialogNegativeClick( + passwordCheckBox!!.isChecked, mMasterPassword, + keyFileCheckBox!!.isChecked, mKeyFile) dismiss() } } @@ -157,18 +157,21 @@ class AssignMasterKeyDialogFragment : DialogFragment() { private fun verifyPassword(): Boolean { var error = false - if (passwordCheckBox!!.isChecked) { - masterPassword = passView!!.text.toString() - val confpass = passConfView!!.text.toString() + if (passwordCheckBox != null + && passwordCheckBox!!.isChecked + && passView != null + && passConfView != null) { + mMasterPassword = passView!!.text.toString() + val confPassword = passConfView!!.text.toString() // Verify that passwords match - if (masterPassword != confpass) { + if (mMasterPassword != confPassword) { error = true // Passwords do not match Toast.makeText(context, R.string.error_pass_match, Toast.LENGTH_LONG).show() } - if (masterPassword == null || masterPassword!!.isEmpty()) { + if (mMasterPassword == null || mMasterPassword!!.isEmpty()) { error = true showEmptyPasswordConfirmationDialog() } @@ -178,12 +181,14 @@ class AssignMasterKeyDialogFragment : DialogFragment() { private fun verifyFile(): Boolean { var error = false - if (keyfileCheckBox!!.isChecked) { - val keyfile = UriUtil.parseDefaultFile(keyfileView!!.text.toString()) - mKeyfile = keyfile + if (keyFileCheckBox != null + && keyFileCheckBox!!.isChecked + && keyFileView != null) { + val keyFile = UriUtil.parseDefaultFile(keyFileView!!.text.toString()) + mKeyFile = keyFile // Verify that a keyfile is set - if (EmptyUtils.isNullOrEmpty(keyfile)) { + if (EmptyUtils.isNullOrEmpty(keyFile)) { error = true Toast.makeText(context, R.string.error_nokeyfile, Toast.LENGTH_LONG).show() } @@ -192,43 +197,47 @@ class AssignMasterKeyDialogFragment : DialogFragment() { } private fun showEmptyPasswordConfirmationDialog() { - val builder = AlertDialog.Builder(activity!!) - builder.setMessage(R.string.warning_empty_password) - .setPositiveButton(android.R.string.ok) { _, _ -> - if (!verifyFile()) { - mListener!!.onAssignKeyDialogPositiveClick( - passwordCheckBox!!.isChecked, masterPassword, - keyfileCheckBox!!.isChecked, mKeyfile) - this@AssignMasterKeyDialogFragment.dismiss() + activity?.let { + val builder = AlertDialog.Builder(it) + builder.setMessage(R.string.warning_empty_password) + .setPositiveButton(android.R.string.ok) { _, _ -> + if (!verifyFile()) { + mListener?.onAssignKeyDialogPositiveClick( + passwordCheckBox!!.isChecked, mMasterPassword, + keyFileCheckBox!!.isChecked, mKeyFile) + this@AssignMasterKeyDialogFragment.dismiss() + } } - } - .setNegativeButton(R.string.cancel) { _, _ -> } - builder.create().show() + .setNegativeButton(R.string.cancel) { _, _ -> } + builder.create().show() + } } private fun showNoKeyConfirmationDialog() { - val builder = AlertDialog.Builder(activity!!) - builder.setMessage(R.string.warning_no_encryption_key) - .setPositiveButton(android.R.string.ok) { _, _ -> - mListener!!.onAssignKeyDialogPositiveClick( - passwordCheckBox!!.isChecked, masterPassword, - keyfileCheckBox!!.isChecked, mKeyfile) - this@AssignMasterKeyDialogFragment.dismiss() - } - .setNegativeButton(R.string.cancel) { _, _ -> } - builder.create().show() + activity?.let { + val builder = AlertDialog.Builder(it) + builder.setMessage(R.string.warning_no_encryption_key) + .setPositiveButton(android.R.string.ok) { _, _ -> + mListener?.onAssignKeyDialogPositiveClick( + passwordCheckBox!!.isChecked, mMasterPassword, + keyFileCheckBox!!.isChecked, mKeyFile) + this@AssignMasterKeyDialogFragment.dismiss() + } + .setNegativeButton(R.string.cancel) { _, _ -> } + builder.create().show() + } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - keyFileHelper!!.onActivityResultCallback(requestCode, resultCode, data + mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data ) { uri -> if (uri != null) { val pathString = UriUtil.parseDefaultFile(uri.toString()) if (pathString != null) { - keyfileCheckBox!!.isChecked = true - keyfileView!!.text = pathString.toString() + keyFileCheckBox?.isChecked = true + keyFileView?.text = pathString.toString() } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java b/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java index 36e241faf..fb75ead1a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java +++ b/app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java @@ -187,7 +187,7 @@ public class CreateFileDialogFragment extends DialogFragment implements AdapterV @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) { + if (data != null && requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) { uriPath = data.getData(); if (uriPath != null) { File file = Utils.getFileForUri(uriPath); diff --git a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt index c2e89f4f0..c24235a37 100644 --- a/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/fileselect/FileDatabaseHistoryAdapter.kt @@ -20,20 +20,13 @@ package com.kunzisoft.keepass.fileselect import android.content.Context -import android.content.res.Resources import android.net.Uri import android.support.annotation.ColorInt import android.support.v7.widget.RecyclerView import android.util.TypedValue -import android.view.ContextMenu -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup +import android.view.* import android.widget.ImageView import android.widget.TextView - import com.kunzisoft.keepass.R import com.kunzisoft.keepass.settings.PreferencesUtil From d937ca85a228818a421aec219f962bca637a860f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 10 Jul 2019 23:37:40 +0200 Subject: [PATCH 145/289] Kotlinized fragments --- .../keepass/activities/EntryActivity.kt | 2 +- .../keepass/activities/EntryEditActivity.kt | 24 +- .../activities/FileDatabaseSelectActivity.kt | 10 +- .../keepass/activities/GroupActivity.kt | 110 ++++----- .../keepass/activities/ListNodesFragment.kt | 2 +- .../keepass/activities/PasswordActivity.kt | 2 +- .../dialogs/AssignMasterKeyDialogFragment.kt | 14 +- .../dialogs/CreateFileDialogFragment.kt | 211 +++++++++++++++++ .../dialogs/GeneratePasswordDialogFragment.kt | 188 +++++++++++++++ .../dialogs/GroupEditDialogFragment.kt | 199 ++++++++++++++++ .../dialogs/IconPickerDialogFragment.kt | 138 +++++++++++ .../KeyboardExplanationDialogFragment.kt | 67 ++++++ .../dialogs/PasswordEncodingDialogHelper.kt | 2 +- .../dialogs/ProFeatureDialogFragment.kt | 75 ++++++ .../activities/dialogs/ReadOnlyDialog.kt | 51 +++++ .../activities/dialogs/SortDialogFragment.kt | 189 +++++++++++++++ .../UnavailableFeatureDialogFragment.kt | 125 ++++++++++ .../UnderDevelopmentFeatureDialogFragment.kt | 85 +++++++ .../view/AddNodeButtonView.java | 6 +- .../view/EntryContentsView.kt | 2 +- .../view/EntryCustomField.java | 2 +- .../view/EntryCustomFieldProtected.java | 2 +- .../view/EntryEditCustomField.java | 2 +- .../dialogs/CreateFileDialogFragment.java | 216 ------------------ .../GeneratePasswordDialogFragment.java | 214 ----------------- .../dialogs/GroupEditDialogFragment.java | 216 ------------------ .../dialogs/IconPickerDialogFragment.java | 152 ------------ .../KeyboardExplanationDialogFragment.java | 75 ------ .../dialogs/ProFeatureDialogFragment.java | 74 ------ .../keepass/dialogs/ReadOnlyDialog.java | 38 --- .../keepass/dialogs/SortDialogFragment.java | 213 ----------------- .../UnavailableFeatureDialogFragment.java | 143 ------------ ...UnderDevelopmentFeatureDialogFragment.java | 85 ------- .../keepass/dialogs/WarningDialog.java | 78 ------- .../settings/NestedSettingsFragment.java | 12 +- app/src/main/res/layout/entry_view.xml | 3 +- .../res/layout/list_nodes_with_add_button.xml | 2 +- 37 files changed, 1423 insertions(+), 1606 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/dialogs/AssignMasterKeyDialogFragment.kt (97%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/CreateFileDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconPickerDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/KeyboardExplanationDialogFragment.kt rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/dialogs/PasswordEncodingDialogHelper.kt (96%) create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ProFeatureDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ReadOnlyDialog.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnavailableFeatureDialogFragment.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnderDevelopmentFeatureDialogFragment.kt rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/view/AddNodeButtonView.java (99%) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/view/EntryContentsView.kt (99%) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/view/EntryCustomField.java (98%) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/view/EntryCustomFieldProtected.java (97%) rename app/src/main/java/com/kunzisoft/keepass/{ => activities}/view/EntryEditCustomField.java (98%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/CreateFileDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/GeneratePasswordDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/GroupEditDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/IconPickerDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/KeyboardExplanationDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/ProFeatureDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/ReadOnlyDialog.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/SortDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/UnavailableFeatureDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/UnderDevelopmentFeatureDialogFragment.java delete mode 100644 app/src/main/java/com/kunzisoft/keepass/dialogs/WarningDialog.java diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index 2d582cfe9..c61f82f62 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -36,6 +36,7 @@ import android.widget.TextView import android.widget.Toast import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.lock.LockingHideActivity +import com.kunzisoft.keepass.activities.view.EntryContentsView import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.database.element.EntryVersioned import com.kunzisoft.keepass.database.element.PwNodeId @@ -48,7 +49,6 @@ import com.kunzisoft.keepass.timeout.ClipboardHelper import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.utils.Util -import com.kunzisoft.keepass.view.EntryContentsView class EntryActivity : LockingHideActivity() { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index 92b042499..0b363cd28 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -29,38 +29,26 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup -import android.widget.EditText -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.ScrollView -import android.widget.Toast - +import android.widget.* import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.dialogs.GeneratePasswordDialogFragment +import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment +import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment.Companion.KEY_ICON_STANDARD import com.kunzisoft.keepass.activities.lock.LockingHideActivity +import com.kunzisoft.keepass.activities.view.EntryEditCustomField import com.kunzisoft.keepass.app.App import com.kunzisoft.keepass.database.action.node.ActionNodeValues import com.kunzisoft.keepass.database.action.node.AddEntryRunnable import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable -import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.database.element.EntryVersioned -import com.kunzisoft.keepass.database.element.GroupVersioned -import com.kunzisoft.keepass.database.element.PwDate -import com.kunzisoft.keepass.database.element.PwIcon -import com.kunzisoft.keepass.database.element.PwIconStandard -import com.kunzisoft.keepass.database.element.PwNodeId +import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.element.security.ProtectedString -import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment -import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment import com.kunzisoft.keepass.education.EntryEditActivityEducation import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.utils.Util -import com.kunzisoft.keepass.view.EntryEditCustomField - -import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPickerListener, GeneratePasswordDialogFragment.GeneratePasswordListener { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 1601201e6..40fbcbec4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -47,8 +47,8 @@ import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable import com.kunzisoft.keepass.database.action.ProgressDialogRunnable -import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment -import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment +import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment +import com.kunzisoft.keepass.activities.dialogs.CreateFileDialogFragment import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation import com.kunzisoft.keepass.fileselect.* import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory @@ -376,8 +376,10 @@ class FileDatabaseSelectActivity : StylishActivity(), } - override fun onDefinePathDialogPositiveClick(pathFile: Uri): Boolean { + override fun onDefinePathDialogPositiveClick(pathFile: Uri?): Boolean { mDatabaseFileUri = pathFile + if (pathFile == null) + return false return if (createDatabaseFile(pathFile)) { AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog") true @@ -385,7 +387,7 @@ class FileDatabaseSelectActivity : StylishActivity(), false } - override fun onDefinePathDialogNegativeClick(pathFile: Uri): Boolean { + override fun onDefinePathDialogNegativeClick(pathFile: Uri?): Boolean { return true } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index f985e8d2c..ec1067b49 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -42,6 +42,7 @@ import android.view.MenuItem import android.view.View import android.widget.ImageView import android.widget.TextView +import android.widget.Toast import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.lock.LockingActivity @@ -62,12 +63,12 @@ import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable import com.kunzisoft.keepass.database.element.* -import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment -import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment -import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment -import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper -import com.kunzisoft.keepass.dialogs.ReadOnlyDialog -import com.kunzisoft.keepass.dialogs.SortDialogFragment +import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment +import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment +import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment +import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogHelper +import com.kunzisoft.keepass.activities.dialogs.ReadOnlyDialog +import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment import com.kunzisoft.keepass.education.GroupActivityEducation import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService import com.kunzisoft.keepass.magikeyboard.KeyboardHelper @@ -77,7 +78,7 @@ import com.kunzisoft.keepass.model.Field import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.utils.MenuUtil -import com.kunzisoft.keepass.view.AddNodeButtonView +import com.kunzisoft.keepass.activities.view.AddNodeButtonView import net.cachapa.expandablelayout.ExpandableLayout @@ -774,54 +775,59 @@ class GroupActivity : LockingActivity(), AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog") } - override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction, - name: String, - icon: PwIcon) { + override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?, + name: String?, + icon: PwIcon?) { val database = App.currentDatabase - when (action) { - GroupEditDialogFragment.EditGroupDialogAction.CREATION -> { - // If group creation - mCurrentGroup?.let { currentGroup -> - // Build the group - database.createGroup()?.let { newGroup-> - newGroup.title = name - newGroup.icon = icon - // Not really needed here because added in runnable but safe - newGroup.parent = currentGroup + if (name.isNullOrEmpty() || icon == null) + Toast.makeText(this, R.string.error_no_name, Toast.LENGTH_LONG).show() + else { + when (action) { + GroupEditDialogFragment.EditGroupDialogAction.CREATION -> { + // If group creation + mCurrentGroup?.let { currentGroup -> + // Build the group + database.createGroup()?.let { newGroup -> + newGroup.title = name + newGroup.icon = icon + // Not really needed here because added in runnable but safe + newGroup.parent = currentGroup - // If group created save it in the database - Thread(AddGroupRunnable(this, - App.currentDatabase, - newGroup, - currentGroup, - AfterAddNodeRunnable(), - !readOnly) - ).start() + // If group created save it in the database + Thread(AddGroupRunnable(this, + App.currentDatabase, + newGroup, + currentGroup, + AfterAddNodeRunnable(), + !readOnly) + ).start() + } } } + GroupEditDialogFragment.EditGroupDialogAction.UPDATE -> + // If update add new elements + mOldGroupToUpdate?.let { oldGroupToUpdate -> + GroupVersioned(oldGroupToUpdate).let { updateGroup -> + updateGroup.title = name + // TODO custom icon + updateGroup.icon = icon + + listNodesFragment?.removeNode(oldGroupToUpdate) + + // If group updated save it in the database + Thread(UpdateGroupRunnable(this, + App.currentDatabase, + oldGroupToUpdate, + updateGroup, + AfterUpdateNodeRunnable(), + !readOnly) + ).start() + } + } + else -> { + } } - GroupEditDialogFragment.EditGroupDialogAction.UPDATE -> - // If update add new elements - mOldGroupToUpdate?.let { oldGroupToUpdate -> - GroupVersioned(oldGroupToUpdate).let { updateGroup -> - updateGroup.title = name - // TODO custom icon - updateGroup.icon = icon - - listNodesFragment?.removeNode(oldGroupToUpdate) - - // If group updated save it in the database - Thread(UpdateGroupRunnable(this, - App.currentDatabase, - oldGroupToUpdate, - updateGroup, - AfterUpdateNodeRunnable(), - !readOnly) - ).start() - } - } - else -> {} } } @@ -874,9 +880,9 @@ class GroupActivity : LockingActivity(), } } - override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction, - name: String, - iconId: PwIcon) { + override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?, + name: String?, + iconId: PwIcon?) { // Do nothing here } 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 7e8b00b5d..01f1096da 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt @@ -20,7 +20,7 @@ import com.kunzisoft.keepass.adapters.NodeAdapter import com.kunzisoft.keepass.database.SortNodeEnum import com.kunzisoft.keepass.database.element.GroupVersioned import com.kunzisoft.keepass.database.element.NodeVersioned -import com.kunzisoft.keepass.dialogs.SortDialogFragment +import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.activities.stylish.StylishFragment 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 98f1dfc42..fbc9f6ab1 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -52,7 +52,7 @@ import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable import com.kunzisoft.keepass.database.action.ProgressDialogRunnable import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper +import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogHelper import com.kunzisoft.keepass.education.PasswordActivityEducation import com.kunzisoft.keepass.fileselect.KeyFileHelper import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector diff --git a/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt similarity index 97% rename from app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt rename to app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt index 490eab2d6..c94a52980 100644 --- a/app/src/main/java/com/kunzisoft/keepass/dialogs/AssignMasterKeyDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt @@ -17,7 +17,7 @@ * along with KeePass DX. If not, see . * */ -package com.kunzisoft.keepass.dialogs +package com.kunzisoft.keepass.activities.dialogs import android.app.Dialog import android.content.Context @@ -57,7 +57,6 @@ class AssignMasterKeyDialogFragment : DialogFragment() { interface AssignPasswordDialogListener { fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?, keyFileChecked: Boolean, keyFile: Uri?) - fun onAssignKeyDialogNegativeClick(masterPasswordChecked: Boolean, masterPassword: String?, keyFileChecked: Boolean, keyFile: Uri?) } @@ -74,10 +73,9 @@ class AssignMasterKeyDialogFragment : DialogFragment() { } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - - activity?.let { notNullActivity -> - val builder = AlertDialog.Builder(notNullActivity) - val inflater = notNullActivity.layoutInflater + activity?.let { activity -> + val builder = AlertDialog.Builder(activity) + val inflater = activity.layoutInflater rootView = inflater.inflate(R.layout.set_password, null) builder.setView(rootView) @@ -126,14 +124,12 @@ class AssignMasterKeyDialogFragment : DialogFragment() { mKeyFile = null var error = verifyPassword() || verifyFile() - if (!passwordCheckBox!!.isChecked && !keyFileCheckBox!!.isChecked) { error = true showNoKeyConfirmationDialog() } - if (!error) { - mListener!!.onAssignKeyDialogPositiveClick( + mListener?.onAssignKeyDialogPositiveClick( passwordCheckBox!!.isChecked, mMasterPassword, keyFileCheckBox!!.isChecked, mKeyFile) dismiss() diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/CreateFileDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/CreateFileDialogFragment.kt new file mode 100644 index 000000000..4984a98d1 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/CreateFileDialogFragment.kt @@ -0,0 +1,211 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.activities.dialogs + +import android.app.Activity +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.support.v4.app.DialogFragment +import android.support.v7.app.AlertDialog +import android.view.ActionMode +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Button +import android.widget.EditText +import android.widget.Spinner +import android.widget.TextView + +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.stylish.FilePickerStylishActivity +import com.kunzisoft.keepass.utils.UriUtil +import com.nononsenseapps.filepicker.FilePickerActivity +import com.nononsenseapps.filepicker.Utils + +class CreateFileDialogFragment : DialogFragment(), AdapterView.OnItemSelectedListener { + + private val FILE_CODE = 3853 + + private var folderPathView: EditText? = null + private var fileNameView: EditText? = null + private var positiveButton: Button? = null + private var negativeButton: Button? = null + + private var mDefinePathDialogListener: DefinePathDialogListener? = null + + private var mDatabaseFileExtension: String? = null + private var mUriPath: Uri? = null + + interface DefinePathDialogListener { + fun onDefinePathDialogPositiveClick(pathFile: Uri?): Boolean + fun onDefinePathDialogNegativeClick(pathFile: Uri?): Boolean + } + + override fun onAttach(activity: Context?) { + super.onAttach(activity) + try { + mDefinePathDialogListener = activity as DefinePathDialogListener? + } catch (e: ClassCastException) { + throw ClassCastException(activity?.toString() + + " must implement " + DefinePathDialogListener::class.java.name) + } + + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + activity?.let { activity -> + val builder = AlertDialog.Builder(activity) + val inflater = activity.layoutInflater + + val rootView = inflater.inflate(R.layout.file_creation, null) + builder.setView(rootView) + .setTitle(R.string.create_keepass_file) + // Add action buttons + .setPositiveButton(android.R.string.ok) { _, _ -> } + .setNegativeButton(R.string.cancel) { _, _ -> } + + // To prevent crash issue #69 https://github.com/Kunzisoft/KeePassDX/issues/69 + val actionCopyBarCallback = object : ActionMode.Callback { + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + positiveButton?.isEnabled = false + negativeButton?.isEnabled = false + return true + } + + override fun onDestroyActionMode(mode: ActionMode) { + positiveButton?.isEnabled = true + negativeButton?.isEnabled = true + } + + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + return true + } + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + return true + } + } + + // Folder selection + val browseView = rootView.findViewById(R.id.browse_button) + folderPathView = rootView.findViewById(R.id.folder_path) + folderPathView?.customSelectionActionModeCallback = actionCopyBarCallback + fileNameView = rootView.findViewById(R.id.filename) + fileNameView?.customSelectionActionModeCallback = actionCopyBarCallback + + val defaultPath = Environment.getExternalStorageDirectory().path + getString(R.string.database_file_path_default) + folderPathView?.setText(defaultPath) + browseView.setOnClickListener { _ -> + Intent(context, FilePickerStylishActivity::class.java).apply { + putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false) + putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true) + putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR) + putExtra(FilePickerActivity.EXTRA_START_PATH, + Environment.getExternalStorageDirectory().path) + startActivityForResult(this, FILE_CODE) + } + } + + // Init path + mUriPath = null + + // Extension + mDatabaseFileExtension = getString(R.string.database_file_extension_default) + val spinner = rootView.findViewById(R.id.file_types) + spinner.onItemSelectedListener = this + + // Spinner Drop down elements + val fileTypes = resources.getStringArray(R.array.file_types) + val dataAdapter = ArrayAdapter(activity!!, android.R.layout.simple_spinner_item, fileTypes) + dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + spinner.adapter = dataAdapter + // Or text if only one item https://github.com/Kunzisoft/KeePassDX/issues/105 + if (fileTypes.size == 1) { + val params = spinner.layoutParams + spinner.visibility = View.GONE + val extensionTextView = TextView(context) + extensionTextView.text = mDatabaseFileExtension + extensionTextView.layoutParams = params + val parentView = spinner.parent as ViewGroup + parentView.addView(extensionTextView) + } + + val dialog = builder.create() + + dialog.setOnShowListener { dialog1 -> + positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE) + negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE) + positiveButton?.setOnClickListener { _ -> + mDefinePathDialogListener?.let { + if (it.onDefinePathDialogPositiveClick(buildPath())) + dismiss() + } + } + negativeButton?.setOnClickListener { _-> + mDefinePathDialogListener?.let { + if (it.onDefinePathDialogNegativeClick(buildPath())) { + dismiss() + } + } + } + } + return dialog + } + return super.onCreateDialog(savedInstanceState) + } + + private fun buildPath(): Uri? { + if (folderPathView != null && mDatabaseFileExtension != null) { + var path = Uri.Builder().path(folderPathView!!.text.toString()) + .appendPath(fileNameView!!.text.toString() + mDatabaseFileExtension!!) + .build() + path = UriUtil.translate(context, path) + return path + } + return null + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) { + mUriPath = data?.data + mUriPath?.let { + val file = Utils.getFileForUri(it) + folderPathView?.setText(file.path) + } + } + } + + override fun onItemSelected(adapterView: AdapterView<*>, view: View, position: Int, id: Long) { + mDatabaseFileExtension = adapterView.getItemAtPosition(position).toString() + } + + override fun onNothingSelected(adapterView: AdapterView<*>) { + // Do nothing + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt new file mode 100644 index 000000000..0c7b6a4a8 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt @@ -0,0 +1,188 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePass DX. + * + * KeePass DX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePass DX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePass DX. If not, see . + * + */ +package com.kunzisoft.keepass.activities.dialogs + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.support.v4.app.DialogFragment +import android.support.v7.app.AlertDialog +import android.view.View +import android.widget.* +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.password.PasswordGenerator +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.utils.Util + +class GeneratePasswordDialogFragment : DialogFragment() { + + private var mListener: GeneratePasswordListener? = null + + private var root: View? = null + private var lengthTextView: EditText? = null + private var passwordView: EditText? = null + + private var uppercaseBox: CompoundButton? = null + private var lowercaseBox: CompoundButton? = null + private var digitsBox: CompoundButton? = null + private var minusBox: CompoundButton? = null + private var underlineBox: CompoundButton? = null + private var spaceBox: CompoundButton? = null + private var specialsBox: CompoundButton? = null + private var bracketsBox: CompoundButton? = null + private var extendedBox: CompoundButton? = null + + override fun onAttach(context: Context?) { + super.onAttach(context) + try { + mListener = context as GeneratePasswordListener? + } catch (e: ClassCastException) { + throw ClassCastException(context?.toString() + + " must implement " + GeneratePasswordListener::class.java.name) + } + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + activity?.let { activity -> + val builder = AlertDialog.Builder(activity) + val inflater = activity.layoutInflater + root = inflater.inflate(R.layout.generate_password, null) + + passwordView = root?.findViewById(R.id.password) + Util.applyFontVisibilityTo(context, passwordView) + + lengthTextView = root?.findViewById(R.id.length) + + uppercaseBox = root?.findViewById(R.id.cb_uppercase) + lowercaseBox = root?.findViewById(R.id.cb_lowercase) + digitsBox = root?.findViewById(R.id.cb_digits) + minusBox = root?.findViewById(R.id.cb_minus) + underlineBox = root?.findViewById(R.id.cb_underline) + spaceBox = root?.findViewById(R.id.cb_space) + specialsBox = root?.findViewById(R.id.cb_specials) + bracketsBox = root?.findViewById(R.id.cb_brackets) + extendedBox = root?.findViewById(R.id.cb_extended) + + assignDefaultCharacters() + + val seekBar = root?.findViewById(R.id.seekbar_length) + seekBar?.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + lengthTextView?.setText(progress.toString()) + } + + override fun onStartTrackingTouch(seekBar: SeekBar) {} + + override fun onStopTrackingTouch(seekBar: SeekBar) {} + }) + seekBar?.progress = PreferencesUtil.getDefaultPasswordLength(context) + + root?.findViewById