diff --git a/CHANGELOG b/CHANGELOG
index 00d67607d..2612ef808 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,12 @@
+KeepassDX (2.5.0.0beta23)
+ * New, more secure database creation workflow
+ * Recognize more database files
+ * Add alias for history files (WARNING: history is erased)
+ * New Biometric unlock (Fingerprint with new API)
+ * Fix entry references
+ * Fix OOM with KeyFile
+ * Fix small issues
+
KeepassDX (2.5.0.0beta22)
* Rebuild code for actions
* Add UUID as entry view
diff --git a/app/.gitignore b/app/.gitignore
index 3cfb122fc..04d6aa0d6 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1 +1,2 @@
+.cxx
.externalNativeBuild
diff --git a/app/build.gradle b/app/build.gradle
index a3480f342..4e3978343 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -4,15 +4,15 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
- compileSdkVersion 27
+ compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.kunzisoft.keepass"
minSdkVersion 14
- targetSdkVersion 27
- versionCode = 22
- versionName = "2.5.0.0beta22"
+ targetSdkVersion 28
+ versionCode = 23
+ versionName = "2.5.0.0beta23"
multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests"
@@ -79,42 +79,35 @@ android {
}
}
-def supportVersion = "27.1.1"
def spongycastleVersion = "1.58.0.0"
-def permissionDispatcherVersion = "3.3.1"
+def room_version = "2.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"
- implementation "com.android.support:preference-v14:$supportVersion"
- implementation "com.android.support:cardview-v7:$supportVersion"
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.preference:preference:1.1.0'
+ implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
+ implementation 'androidx.cardview:cardview:1.0.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'androidx.biometric:biometric:1.0.0-beta01'
+ implementation 'com.google.android.material:material:1.0.0'
+
+ implementation "androidx.room:room-runtime:$room_version"
+ kapt "androidx.room:room-compiler:$room_version"
+
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
// Expandable view
implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2'
// Time
implementation 'joda-time:joda-time:2.9.9'
- implementation 'org.sufficientlysecure:html-textview:3.5'
- implementation 'com.nononsenseapps:filepicker:4.1.0'
+ // Education
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0'
- // Permissions
- implementation("com.github.hotchemi:permissionsdispatcher:$permissionDispatcherVersion") {
- // if you don't use android.app.Fragment you can exclude support for them
- exclude module: "support-v13"
- }
- 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'
// Base64
implementation 'biz.source_code:base64coder:2010-12-19'
- // IO-Extras
- implementation 'com.github.davidmoten:io-extras:0.1'
- implementation 'com.google.code.gson:gson:2.8.4'
- implementation 'com.google.guava:guava:23.0-android'
// Icon pack
implementation project(path: ':icon-pack-classic')
implementation project(path: ':icon-pack-material')
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/AccentTest.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/AccentTest.java
deleted file mode 100644
index 20b1a5d83..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/AccentTest.java
+++ /dev/null
@@ -1,44 +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.tests;
-
-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);
- }
- */
- }
-
-}
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/AllTests.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/AllTests.java
deleted file mode 100644
index 434cf5011..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/AllTests.java
+++ /dev/null
@@ -1,34 +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.tests;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.test.suitebuilder.TestSuiteBuilder;
-
-public class AllTests extends TestSuite {
-
- public static Test suite() {
- return new TestSuiteBuilder(AllTests.class)
- .includeAllPackagesUnderHere()
- .build();
- }
-}
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/OutputTests.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/OutputTests.java
deleted file mode 100644
index dde340c2b..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/OutputTests.java
+++ /dev/null
@@ -1,35 +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.tests;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.test.suitebuilder.TestSuiteBuilder;
-
-public class OutputTests extends TestSuite {
-
- public static Test suite() {
-
- return new TestSuiteBuilder(AllTests.class)
- .includePackages("com.kunzisoft.keepass.tests.output")
- .build();
- }
-}
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.java
deleted file mode 100644
index 8ec7691f4..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV3.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.tests;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Calendar;
-
-
-import android.test.AndroidTestCase;
-
-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);
- }
-}
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java
deleted file mode 100644
index ab1b7737f..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwEntryTestV4.java
+++ /dev/null
@@ -1,59 +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.tests;
-
-import junit.framework.TestCase;
-
-public class PwEntryTestV4 extends TestCase {
- public void testAssign() {
- /*
- TODO Test
- PwEntryV4 entry = new PwEntryV4();
-
- entry.setAdditional("test223");
-
- entry.setAutoType(new AutoType());
- entry.getAutoType().defaultSequence = "1324";
- entry.getAutoType().enabled = true;
- entry.getAutoType().obfuscationOptions = 123412432109L;
- entry.getAutoType().put("key", "value");
-
- 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");
- entry.addToHistory(new PwEntryV4());
- entry.setIconStandard(new PwIconStandard(5));
- entry.setOverrideURL("override");
- entry.setParent(new PwGroupV4());
- entry.addExtraField("key2", new ProtectedString(false, "value2"));
- entry.setUrl("http://localhost");
- entry.setNodeId(UUID.randomUUID());
-
- PwEntryV4 target = new PwEntryV4();
- target.updateWith(entry);
-
- /* 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
deleted file mode 100644
index 097b2f294..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/PwGroupTest.java
+++ /dev/null
@@ -1,44 +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.tests;
-
-
-import android.test.AndroidTestCase;
-
-import com.kunzisoft.keepass.database.element.PwGroupV3;
-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"));
- }
-}
-
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.java
deleted file mode 100644
index 542f69e4b..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TestUtil.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.tests;
-
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.os.Environment;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-
-public class TestUtil {
- 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 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
deleted file mode 100644
index 66f792b8b..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.java
+++ /dev/null
@@ -1,211 +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.tests;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.io.ByteArrayOutputStream;
-import java.util.Calendar;
-import java.util.Random;
-import java.util.UUID;
-
-import junit.framework.TestCase;
-
-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;
-
-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 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.Companion.writeTime(expected.getTime(), cal);
- Calendar actual = Calendar.getInstance();
- actual.setTime(PwDate.Companion.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/TypesTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.kt
new file mode 100644
index 000000000..5a353e858
--- /dev/null
+++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/TypesTest.kt
@@ -0,0 +1,210 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals
+
+import java.io.ByteArrayOutputStream
+import java.util.Calendar
+import java.util.Random
+
+import junit.framework.TestCase
+
+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
+
+class TypesTest : TestCase() {
+
+ fun testReadWriteLongZero() {
+ testReadWriteLong(0.toByte())
+ }
+
+ fun testReadWriteLongMax() {
+ testReadWriteLong(java.lang.Byte.MAX_VALUE)
+ }
+
+ fun testReadWriteLongMin() {
+ testReadWriteLong(java.lang.Byte.MIN_VALUE)
+ }
+
+ fun testReadWriteLongRnd() {
+ val rnd = Random()
+ val buf = ByteArray(1)
+ rnd.nextBytes(buf)
+
+ testReadWriteLong(buf[0])
+ }
+
+ private fun testReadWriteLong(value: Byte) {
+ val orig = ByteArray(8)
+ val dest = ByteArray(8)
+
+ setArray(orig, value, 0, 8)
+
+ val one = LEDataInputStream.readLong(orig, 0)
+ LEDataOutputStream.writeLong(one, dest, 0)
+
+ assertArrayEquals(orig, dest)
+
+ }
+
+ fun testReadWriteIntZero() {
+ testReadWriteInt(0.toByte())
+ }
+
+ fun testReadWriteIntMin() {
+ testReadWriteInt(java.lang.Byte.MIN_VALUE)
+ }
+
+ fun testReadWriteIntMax() {
+ testReadWriteInt(java.lang.Byte.MAX_VALUE)
+ }
+
+ private fun testReadWriteInt(value: Byte) {
+ val orig = ByteArray(4)
+ val dest = ByteArray(4)
+
+ for (i in 0..3) {
+ orig[i] = 0
+ }
+
+ setArray(orig, value, 0, 4)
+
+ val one = LEDataInputStream.readInt(orig, 0)
+
+ LEDataOutputStream.writeInt(one, dest, 0)
+
+ assertArrayEquals(orig, dest)
+
+ }
+
+ private fun setArray(buf: ByteArray, value: Byte, offset: Int, size: Int) {
+ for (i in offset until offset + size) {
+ buf[i] = value
+ }
+ }
+
+ fun testReadWriteShortOne() {
+ val orig = ByteArray(2)
+
+ orig[0] = 0
+ orig[1] = 1
+
+ val one = LEDataInputStream.readUShort(orig, 0)
+ val dest = LEDataOutputStream.writeUShortBuf(one)
+
+ assertArrayEquals(orig, dest)
+
+ }
+
+ fun testReadWriteShortMin() {
+ testReadWriteShort(java.lang.Byte.MIN_VALUE)
+ }
+
+ fun testReadWriteShortMax() {
+ testReadWriteShort(java.lang.Byte.MAX_VALUE)
+ }
+
+ private fun testReadWriteShort(value: Byte) {
+ val orig = ByteArray(2)
+ val dest = ByteArray(2)
+
+ setArray(orig, value, 0, 2)
+
+ val one = LEDataInputStream.readUShort(orig, 0)
+ LEDataOutputStream.writeUShort(one, dest, 0)
+
+ assertArrayEquals(orig, dest)
+
+ }
+
+ fun testReadWriteByteZero() {
+ testReadWriteByte(0.toByte())
+ }
+
+ fun testReadWriteByteMin() {
+ testReadWriteByte(java.lang.Byte.MIN_VALUE)
+ }
+
+ fun testReadWriteByteMax() {
+ testReadWriteShort(java.lang.Byte.MAX_VALUE)
+ }
+
+ private fun testReadWriteByte(value: Byte) {
+ val orig = ByteArray(1)
+ val dest = ByteArray(1)
+
+ setArray(orig, value, 0, 1)
+
+ val one = Types.readUByte(orig, 0)
+ Types.writeUByte(one, dest, 0)
+
+ assertArrayEquals(orig, dest)
+
+ }
+
+ fun testDate() {
+ val cal = Calendar.getInstance()
+
+ val expected = Calendar.getInstance()
+ expected.set(2008, 1, 2, 3, 4, 5)
+
+ val buf = PwDate.writeTime(expected.time, cal)
+ val actual = Calendar.getInstance()
+ actual.time = 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))
+ }
+
+ fun testUUID() {
+ val rnd = Random()
+ val bUUID = ByteArray(16)
+ rnd.nextBytes(bUUID)
+
+ val uuid = Types.bytestoUUID(bUUID)
+ val eUUID = Types.UUIDtoBytes(uuid)
+
+ assertArrayEquals("UUID match failed", bUUID, eUUID)
+ }
+
+ @Throws(Exception::class)
+ fun testULongMax() {
+ val ulongBytes = ByteArray(8)
+ for (i in ulongBytes.indices) {
+ ulongBytes[i] = -1
+ }
+
+ val bos = ByteArrayOutputStream()
+ val leos = LEDataOutputStream(bos)
+ leos.writeLong(Types.ULONG_MAX_VALUE)
+ leos.close()
+
+ val 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
deleted file mode 100644
index b439b28a4..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/AESTest.java
+++ /dev/null
@@ -1,83 +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.tests.crypto;
-
-import com.kunzisoft.keepass.crypto.CipherFactory;
-
-import junit.framework.TestCase;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Random;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.spec.IvParameterSpec;
-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.INSTANCE.getInstance("AES/CBC/PKCS5Padding", true);
- android.init(Cipher.ENCRYPT_MODE, key, iv);
- byte[] outAndroid = android.doFinal(input, 0, dataSize);
-
- Cipher nat = CipherFactory.INSTANCE.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/AESTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/AESTest.kt
new file mode 100644
index 000000000..590914330
--- /dev/null
+++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/AESTest.kt
@@ -0,0 +1,85 @@
+/*
+ * 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
+
+import junit.framework.TestCase
+
+import java.security.InvalidAlgorithmParameterException
+import java.security.InvalidKeyException
+import java.security.NoSuchAlgorithmException
+import java.util.Random
+
+import javax.crypto.BadPaddingException
+import javax.crypto.Cipher
+import javax.crypto.IllegalBlockSizeException
+import javax.crypto.NoSuchPaddingException
+import javax.crypto.spec.IvParameterSpec
+import javax.crypto.spec.SecretKeySpec
+
+import org.junit.Assert.assertArrayEquals
+
+class AESTest : TestCase() {
+
+ private val mRand = Random()
+
+ @Throws(InvalidKeyException::class, NoSuchAlgorithmException::class, NoSuchPaddingException::class, IllegalBlockSizeException::class, BadPaddingException::class, InvalidAlgorithmParameterException::class)
+ fun testEncrypt() {
+ // Test above below and at the blocksize
+ testFinal(15)
+ testFinal(16)
+ testFinal(17)
+
+ // Test random larger sizes
+ val size = mRand.nextInt(494) + 18
+ testFinal(size)
+ }
+
+ @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, IllegalBlockSizeException::class, BadPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
+ private fun testFinal(dataSize: Int) {
+
+ // Generate some input
+ val input = ByteArray(dataSize)
+ mRand.nextBytes(input)
+
+ // Generate key
+ val keyArray = ByteArray(32)
+ mRand.nextBytes(keyArray)
+ val key = SecretKeySpec(keyArray, "AES")
+
+ // Generate IV
+ val ivArray = ByteArray(16)
+ mRand.nextBytes(ivArray)
+ val iv = IvParameterSpec(ivArray)
+
+ val android = CipherFactory.getInstance("AES/CBC/PKCS5Padding", true)
+ android.init(Cipher.ENCRYPT_MODE, key, iv)
+ val outAndroid = android.doFinal(input, 0, dataSize)
+
+ val nat = CipherFactory.getInstance("AES/CBC/PKCS5Padding")
+ nat.init(Cipher.ENCRYPT_MODE, key, iv)
+ val 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
deleted file mode 100644
index ce2906456..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/CipherTest.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.tests.crypto;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Random;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-
-import junit.framework.TestCase;
-
-import com.kunzisoft.keepass.crypto.CipherFactory;
-import com.kunzisoft.keepass.crypto.engine.AesEngine;
-import com.kunzisoft.keepass.crypto.engine.CipherEngine;
-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.INSTANCE.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.INSTANCE.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/CipherTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/CipherTest.kt
new file mode 100644
index 000000000..0d19ba794
--- /dev/null
+++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/CipherTest.kt
@@ -0,0 +1,102 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals
+
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+import java.security.InvalidAlgorithmParameterException
+import java.security.InvalidKeyException
+import java.security.NoSuchAlgorithmException
+import java.util.Random
+
+import javax.crypto.BadPaddingException
+import javax.crypto.Cipher
+import javax.crypto.CipherOutputStream
+import javax.crypto.IllegalBlockSizeException
+import javax.crypto.NoSuchPaddingException
+
+import junit.framework.TestCase
+
+import com.kunzisoft.keepass.crypto.CipherFactory
+import com.kunzisoft.keepass.crypto.engine.AesEngine
+import com.kunzisoft.keepass.crypto.engine.CipherEngine
+import com.kunzisoft.keepass.stream.BetterCipherInputStream
+import com.kunzisoft.keepass.stream.LEDataInputStream
+
+class CipherTest : TestCase() {
+ private val rand = Random()
+
+ @Throws(InvalidKeyException::class, NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidAlgorithmParameterException::class, IllegalBlockSizeException::class, BadPaddingException::class)
+ fun testCipherFactory() {
+ val key = ByteArray(32)
+ val iv = ByteArray(16)
+
+ val plaintext = ByteArray(1024)
+
+ rand.nextBytes(key)
+ rand.nextBytes(iv)
+ rand.nextBytes(plaintext)
+
+ val aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID)
+ val encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv)
+ val decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv)
+
+ val secrettext = encrypt.doFinal(plaintext)
+ val decrypttext = decrypt.doFinal(secrettext)
+
+ assertArrayEquals("Encryption and decryption failed", plaintext, decrypttext)
+ }
+
+ @Throws(InvalidKeyException::class, NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidAlgorithmParameterException::class, IllegalBlockSizeException::class, BadPaddingException::class, IOException::class)
+ fun testCipherStreams() {
+ val MESSAGE_LENGTH = 1024
+
+ val key = ByteArray(32)
+ val iv = ByteArray(16)
+
+ val plaintext = ByteArray(MESSAGE_LENGTH)
+
+ rand.nextBytes(key)
+ rand.nextBytes(iv)
+ rand.nextBytes(plaintext)
+
+ val aes = CipherFactory.getInstance(AesEngine.CIPHER_UUID)
+ val encrypt = aes.getCipher(Cipher.ENCRYPT_MODE, key, iv)
+ val decrypt = aes.getCipher(Cipher.DECRYPT_MODE, key, iv)
+
+ val bos = ByteArrayOutputStream()
+ val cos = CipherOutputStream(bos, encrypt)
+ cos.write(plaintext)
+ cos.close()
+
+ val secrettext = bos.toByteArray()
+
+ val bis = ByteArrayInputStream(secrettext)
+ val cis = BetterCipherInputStream(bis, decrypt)
+ val lis = LEDataInputStream(cis)
+
+ val 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
deleted file mode 100644
index 2c36c9bac..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.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.tests.crypto;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.io.IOException;
-import java.util.Random;
-
-import junit.framework.TestCase;
-
-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);
-
- }
-}
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.kt
new file mode 100644
index 000000000..ce98f6db9
--- /dev/null
+++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/crypto/FinalKeyTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals
+
+import java.io.IOException
+import java.util.Random
+
+import junit.framework.TestCase
+
+import com.kunzisoft.keepass.crypto.finalkey.AndroidFinalKey
+import com.kunzisoft.keepass.crypto.finalkey.NativeFinalKey
+
+class FinalKeyTest : TestCase() {
+ private var mRand: Random? = null
+
+ @Throws(Exception::class)
+ override fun setUp() {
+ super.setUp()
+
+ mRand = Random()
+ }
+
+ @Throws(IOException::class)
+ fun testNativeAndroid() {
+ // Test both an old and an even number to test my flip variable
+ testNativeFinalKey(5)
+ testNativeFinalKey(6)
+ }
+
+ @Throws(IOException::class)
+ private fun testNativeFinalKey(rounds: Int) {
+ val seed = ByteArray(32)
+ val key = ByteArray(32)
+ val nativeKey: ByteArray
+ val androidKey: ByteArray
+
+ mRand!!.nextBytes(seed)
+ mRand!!.nextBytes(key)
+
+ val aKey = AndroidFinalKey()
+ androidKey = aKey.transformMasterKey(seed, key, rounds.toLong())
+
+ val nKey = NativeFinalKey()
+ nativeKey = nKey.transformMasterKey(seed, key, rounds.toLong())
+
+ 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
deleted file mode 100644
index ed01c2334..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/DeleteEntry.java
+++ /dev/null
@@ -1,111 +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.tests.database;
-
-import android.test.AndroidTestCase;
-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.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() {
-
- /*
- Database db;
-
- Context ctx = getContext();
-
- try {
- db = TestData.GetDb(ctx, ASSET, PASSWORD, KEYFILE, FILENAME);
- } catch (Exception e) {
- assertTrue("Failed to open database: " + e.getMessage(), false);
- return;
- }
-
- PwDatabaseV3 pm = (PwDatabaseV3) db.getPwDatabase();
- GroupVersioned group1 = getGroup(pm, GROUP1_NAME);
- assertNotNull("Could not find group1", group1);
-
- // Delete the group
- DeleteGroupRunnable task = new DeleteGroupRunnable(null, db, group1, null, true);
- task.run();
-
- // Verify the entries were deleted
- PwEntryInterface entry1 = getEntry(pm, ENTRY1_NAME);
- assertNull("Entry 1 was not removed", entry1);
-
- 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);
- 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());
-
- // Verify the group was deleted
- group1 = getGroup(pm, GROUP1_NAME);
- assertNull("Group 1 was not removed.", group1);
- */
-
- }
-
- 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.getTitle().equals(name) ) {
- return entry;
- }
- }
- */
- return null;
-
- }
-
- private GroupVersioned getGroup(PwDatabase pm, String name) {
- /*
- List groups = pm.getGroups();
- for ( int i = 0; i < groups.size(); i++ ) {
- GroupVersioned group = groups.get(i);
- if ( group.getTitle().equals(name) ) {
- 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
deleted file mode 100644
index fa009c76f..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/EntryV4.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.tests.database;
-
-import com.kunzisoft.keepass.database.element.PwDatabaseV4;
-import com.kunzisoft.keepass.database.element.PwEntryV4;
-
-import junit.framework.TestCase;
-
-public class EntryV4 extends TestCase {
-
- public void testBackup() {
- /*
- PwDatabaseV4 db = new PwDatabaseV4();
-
- db.setHistoryMaxItems(2);
-
- PwEntryV4 entry = new PwEntryV4();
- entry.startToManageFieldReferences(db);
- entry.setTitle("Title1");
- entry.setUsername("User1");
- entry.createBackup(db);
-
- entry.setTitle("Title2");
- entry.setUsername("User2");
- entry.createBackup(db);
-
- entry.setTitle("Title3");
- entry.setUsername("User3");
- entry.createBackup(db);
-
- PwEntryV4 backup = entry.getHistory().get(0);
- entry.stopToManageFieldReferences();
- 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
deleted file mode 100644
index 46121d9db..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3.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.tests.database;
-
-import android.test.AndroidTestCase;
-
-public class Kdb3 extends AndroidTestCase {
-
- private void testKeyfile(String dbAsset, String keyAsset, String password) throws Exception {
- /*
- Context ctx = getContext();
-
- File sdcard = Environment.getExternalStorageDirectory();
- String keyPath = sdcard.getAbsolutePath() + "/key";
-
- TestUtil.extractKey(ctx, keyAsset, keyPath);
-
- AssetManager am = ctx.getAssets();
- InputStream is = am.open(dbAsset, AssetManager.ACCESS_STREAMING);
-
- ImporterV3 importer = new ImporterV3();
- importer.openDatabase(is, password, TestUtil.getKeyFileInputStream(ctx, keyPath));
-
- 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");
- }
-
-}
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
deleted file mode 100644
index 6c1105909..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb3Twofish.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.tests.database;
-
-import android.test.AndroidTestCase;
-
-public class Kdb3Twofish extends AndroidTestCase {
- public void testReadTwofish() throws Exception {
- /*
- Context ctx = getContext();
-
- AssetManager am = ctx.getAssets();
- InputStream is = am.open("twofish.kdb", AssetManager.ACCESS_STREAMING);
-
- ImporterV3 importer = new ImporterV3();
-
- PwDatabaseV3 db = importer.openDatabase(is, "12345", null);
-
- assertTrue(db.getEncryptionAlgorithm() == PwEncryptionAlgorithm.Twofish);
-
- is.close();
- */
- }
-}
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
deleted file mode 100644
index 74e6323e2..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4.java
+++ /dev/null
@@ -1,171 +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.tests.database;
-
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.test.AndroidTestCase;
-
-import com.kunzisoft.keepass.database.exception.InvalidDBException;
-import com.kunzisoft.keepass.database.exception.PwDbOutputException;
-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 {
- Context ctx = getContext();
-
- 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 {
- Context ctx = getContext();
-
- 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 {
- testSaving("test.kdbx", "12345", "test-out.kdbx");
- }
-
- public void testSavingKDBXV4() throws IOException, InvalidDBException, PwDbOutputException {
- testSaving("test-kdbxv4.kdbx", "1", "test-kdbxv4-out.kdbx");
- }
-
- private void testSaving(String inputFile, String password, String outputFile) throws IOException, InvalidDBException, PwDbOutputException {
- Context ctx = getContext();
-
- 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();
-
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- PwDbV4Output output = (PwDbV4Output) PwDbOutput.getInstance(db, bos);
- output.output();
-
- byte[] data = bos.toByteArray();
-
- FileOutputStream fos = new FileOutputStream(TestUtil.getSdPath(outputFile), false);
-
- InputStream bis = new ByteArrayInputStream(data);
- bis = new CopyInputStream(bis, fos);
- importer = new ImporterV4();
- db = importer.openDatabase(bis, password, null);
- bis.close();
-
- fos.close();
- */
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- TestUtil.extractKey(getContext(), "keyfile.key", TestUtil.getSdPath("key"));
- TestUtil.extractKey(getContext(), "binary.key", TestUtil.getSdPath("key-binary"));
- }
-
- public void testComposite() throws IOException, InvalidDBException {
- Context ctx = getContext();
-
- 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();
- */
-
- }
-
- public void testCompositeBinary() throws IOException, InvalidDBException {
- Context ctx = getContext();
-
- 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 {
- Context ctx = getContext();
-
- 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 {
- Context ctx = getContext();
-
- 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
deleted file mode 100644
index 99ca1a9d1..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/Kdb4Header.java
+++ /dev/null
@@ -1,49 +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.tests.database;
-
-import android.content.Context;
-import android.content.res.AssetManager;
-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);
-
- /*
- TODO Test
- ImporterV4 importer = new ImporterV4();
-
- PwDatabaseV4 db = importer.openDatabase(is, "12345", null);
-
- assertEquals(6000, db.getNumberKeyEncryptionRounds());
-
- 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
deleted file mode 100644
index bfa06f696..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/SprEngineTest.java
+++ /dev/null
@@ -1,70 +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.tests.database;
-
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.test.AndroidTestCase;
-
-import com.kunzisoft.keepass.database.element.PwDatabaseV4;
-import com.kunzisoft.keepass.database.element.SprEngineV4;
-
-import java.io.InputStream;
-
-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);
-
- /*
- 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}";
- 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.getEntryById(entryUUID);
-
-
- assertEquals(RESULT, spr.compile(REF, entry, db));
- */
-
- }
-}
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
deleted file mode 100644
index 0d4dee7e6..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/database/TestData.java
+++ /dev/null
@@ -1,71 +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.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 Database mDb1;
-
- /*
-
- public static Database GetDb1(Context ctx) throws Exception {
- return GetDb1(ctx, false);
- }
-
- public static Database GetDb1(Context ctx, boolean forceReload) throws Exception {
- if ( mDb1 == null || forceReload ) {
- mDb1 = GetDb(ctx, TEST1_KDB, TEST1_PASSWORD, TEST1_KEYFILE, "/sdcard/test1.kdb");
- }
-
- return mDb1;
- }
-
- public static Database GetDb(Context ctx, String asset, String password, String keyfile, String filename) throws Exception {
- AssetManager am = ctx.getAssets();
- InputStream is = am.open(asset, AssetManager.ACCESS_STREAMING);
-
- Database Db = new Database();
-
- InputStream keyIs = TestUtil.getKeyFileInputStream(ctx, keyfile);
-
- Db.loadData(ctx, is, password, keyIs, Importer.DEBUG);
- Uri.Builder b = new Uri.Builder();
-
- Db.setUri(b.scheme("file").path(filename).build());
-
- return Db;
-
- }
-
- public static PwDatabaseV3Debug GetTest1(Context ctx) throws Exception {
- if ( mDb1 == null ) {
- GetDb1(ctx);
- }
-
- //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
deleted file mode 100644
index aa77c7843..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/output/PwManagerOutputTest.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.tests.output;
-
-import android.test.AndroidTestCase;
-
-public class PwManagerOutputTest extends AndroidTestCase {
- // PwDatabaseV3Debug mPM;
-
- /*
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mPM = TestData.GetTest1(getContext());
- }
-
- public void testPlainContent() throws IOException, PwDbOutputException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- PwDbV3Output pos = new PwDbV3OutputDebug(mPM, bos, true);
- pos.outputPlanGroupAndEntries(bos);
-
- assertTrue("No output", bos.toByteArray().length > 0);
- assertArrayEquals("Group and entry output doesn't match.", mPM.getPostHeader(), bos.toByteArray());
-
- }
-
- public void testChecksum() throws NoSuchAlgorithmException, IOException, PwDbOutputException {
- //FileOutputStream fos = new FileOutputStream("/dev/null");
- NullOutputStream nos = new NullOutputStream();
- MessageDigest md = MessageDigest.getInstance("SHA-256");
-
- DigestOutputStream dos = new DigestOutputStream(nos, md);
-
- PwDbV3Output pos = new PwDbV3OutputDebug(mPM, dos, true);
- pos.outputPlanGroupAndEntries(dos);
- dos.close();
-
- byte[] digest = md.digest();
- assertTrue("No output", digest.length > 0);
- assertArrayEquals("Hash of groups and entries failed.", mPM.getDbHeader().contentsHash, digest);
- }
-
- private void assertHeadersEquals(PwDbHeaderV3 expected, PwDbHeaderV3 actual) {
- assertEquals("Flags unequal", expected.flags, actual.flags);
- assertEquals("Entries unequal", expected.numEntries, actual.numEntries);
- assertEquals("Groups unequal", expected.numGroups, actual.numGroups);
- assertEquals("Key Rounds unequal", expected.numKeyEncRounds, actual.numKeyEncRounds);
- assertEquals("Signature1 unequal", expected.signature1, actual.signature1);
- assertEquals("Signature2 unequal", expected.signature2, actual.signature2);
- assertTrue("Version incompatible", PwDbHeaderV3.compatibleHeaders(expected.version, actual.version));
- assertArrayEquals("Hash unequal", expected.contentsHash, actual.contentsHash);
- assertArrayEquals("IV unequal", expected.encryptionIV, actual.encryptionIV);
- assertArrayEquals("Seed unequal", expected.masterSeed, actual.masterSeed);
- assertArrayEquals("Seed2 unequal", expected.transformSeed, actual.transformSeed);
- }
-
- public void testHeader() throws PwDbOutputException, IOException {
- ByteArrayOutputStream bActual = new ByteArrayOutputStream();
- PwDbV3Output pActual = new PwDbV3OutputDebug(mPM, bActual, true);
- PwDbHeaderV3 header = pActual.outputHeader(bActual);
-
- ByteArrayOutputStream bExpected = new ByteArrayOutputStream();
- PwDbHeaderOutputV3 outExpected = new PwDbHeaderOutputV3(mPM.getDbHeader(), bExpected);
- outExpected.output();
-
- assertHeadersEquals(mPM.getDbHeader(), header);
- assertTrue("No output", bActual.toByteArray().length > 0);
- assertArrayEquals("Header does not match.", bExpected.toByteArray(), bActual.toByteArray());
- }
-
- public void testFinalKey() throws PwDbOutputException {
- ByteArrayOutputStream bActual = new ByteArrayOutputStream();
- PwDbV3Output pActual = new PwDbV3OutputDebug(mPM, bActual, true);
- PwDbHeader hActual = pActual.outputHeader(bActual);
- byte[] finalKey = pActual.getFinalKey(hActual);
-
- assertArrayEquals("Keys mismatched", mPM.getFinalKey(), finalKey);
-
- }
-
- public void testFullWrite() throws IOException, PwDbOutputException {
- AssetManager am = getContext().getAssets();
- InputStream is = am.open("test1.kdb");
-
- // Pull file into byte array (for streaming fun)
- ByteArrayOutputStream bExpected = new ByteArrayOutputStream();
- while (true) {
- int data = is.read();
- if ( data == -1 ) {
- break;
- }
- bExpected.write(data);
- }
-
- ByteArrayOutputStream bActual = new ByteArrayOutputStream();
- PwDbV3Output pActual = new PwDbV3OutputDebug(mPM, bActual, true);
- pActual.output();
- //pActual.close();
-
- FileOutputStream fos = new FileOutputStream(TestUtil.getSdPath("test1_out.kdb"));
- fos.write(bActual.toByteArray());
- fos.close();
- assertArrayEquals("Databases do not match.", bExpected.toByteArray(), bActual.toByteArray());
-
- }
- */
-}
\ No newline at end of file
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
deleted file mode 100644
index 68b221322..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/search/SearchTest.java
+++ /dev/null
@@ -1,70 +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.tests.search;
-
-
-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.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();
-
- }
-}
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
deleted file mode 100644
index ed14b8552..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/HashedBlock.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.tests.stream;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Random;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-
-import junit.framework.TestCase;
-
-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);
-
- 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/stream/HashedBlock.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/HashedBlock.kt
new file mode 100644
index 000000000..4e80c5226
--- /dev/null
+++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/stream/HashedBlock.kt
@@ -0,0 +1,117 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals
+
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+import java.util.Random
+import java.util.zip.GZIPInputStream
+import java.util.zip.GZIPOutputStream
+
+import junit.framework.TestCase
+
+import com.kunzisoft.keepass.stream.HashedBlockInputStream
+import com.kunzisoft.keepass.stream.HashedBlockOutputStream
+
+class HashedBlock : TestCase() {
+
+ @Throws(IOException::class)
+ fun testBlockAligned() {
+ testSize(1024, 1024)
+ }
+
+ @Throws(IOException::class)
+ fun testOffset() {
+ testSize(1500, 1024)
+ }
+
+ @Throws(IOException::class)
+ private fun testSize(blockSize: Int, bufferSize: Int) {
+ val orig = ByteArray(blockSize)
+
+ rand.nextBytes(orig)
+
+ val bos = ByteArrayOutputStream()
+ val output = HashedBlockOutputStream(bos, bufferSize)
+ output.write(orig)
+ output.close()
+
+ val encoded = bos.toByteArray()
+
+ val bis = ByteArrayInputStream(encoded)
+ val input = HashedBlockInputStream(bis)
+
+ val decoded = ByteArrayOutputStream()
+ while (true) {
+ val buf = ByteArray(1024)
+ val read = input.read(buf)
+ if (read == -1) {
+ break
+ }
+
+ decoded.write(buf, 0, read)
+ }
+
+ val out = decoded.toByteArray()
+
+ assertArrayEquals(orig, out)
+
+ }
+
+ @Throws(IOException::class)
+ fun testGZIPStream() {
+ val testLength = 32000
+
+ val orig = ByteArray(testLength)
+ rand.nextBytes(orig)
+
+ val bos = ByteArrayOutputStream()
+ val hos = HashedBlockOutputStream(bos)
+ val zos = GZIPOutputStream(hos)
+
+ zos.write(orig)
+ zos.close()
+
+ val compressed = bos.toByteArray()
+ val bis = ByteArrayInputStream(compressed)
+ val his = HashedBlockInputStream(bis)
+ val zis = GZIPInputStream(his)
+
+ val uncompressed = ByteArray(testLength)
+
+ var read = 0
+ while (read != -1 && testLength - read > 0) {
+ read += zis.read(uncompressed, read, testLength - read)
+
+ }
+
+ assertArrayEquals("Output not equal to input", orig, uncompressed)
+
+
+ }
+
+ companion object {
+
+ private val rand = Random()
+ }
+}
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
deleted file mode 100644
index 12151eadd..000000000
--- a/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.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.tests.utils;
-
-import java.util.Locale;
-
-import com.kunzisoft.keepass.utils.StringUtil;
-
-import junit.framework.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, StringUtil.INSTANCE.indexOfIgnoreCase(text, search, Locale.ENGLISH));
- }
-
- 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 testReplaceAllIgnoresCase2() {
- assertEquals(repText, StringUtil.INSTANCE.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH));
- }
-}
diff --git a/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.kt b/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.kt
new file mode 100644
index 000000000..d681c63ed
--- /dev/null
+++ b/app/src/androidTest/java/com/kunzisoft/keepass/tests/utils/StringUtilTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.utils
+
+import java.util.Locale
+
+import com.kunzisoft.keepass.utils.StringUtil
+
+import junit.framework.TestCase
+
+class StringUtilTest : TestCase() {
+ private val text = "AbCdEfGhIj"
+ private val search = "BcDe"
+ private val badSearch = "Ed"
+
+ private val repText = "AbCtestingaBc"
+ private val repSearch = "ABc"
+ private val repSearchBad = "CCCCCC"
+ private val repNew = "12345"
+ private val repResult = "12345testing12345"
+
+ fun testIndexOfIgnoreCase1() {
+ assertEquals(1, StringUtil.indexOfIgnoreCase(text, search, Locale.ENGLISH))
+ }
+
+ fun testIndexOfIgnoreCase2() {
+ assertEquals(-1f, StringUtil.indexOfIgnoreCase(text, search, Locale.ENGLISH).toFloat(), 2f)
+ }
+
+ fun testIndexOfIgnoreCase3() {
+ assertEquals(-1, StringUtil.indexOfIgnoreCase(text, badSearch, Locale.ENGLISH))
+ }
+
+ fun testReplaceAllIgnoresCase1() {
+ assertEquals(repResult, StringUtil.replaceAllIgnoresCase(repText, repSearch, repNew, Locale.ENGLISH))
+ }
+
+ fun testReplaceAllIgnoresCase2() {
+ assertEquals(repText, StringUtil.replaceAllIgnoresCase(repText, repSearchBad, repNew, Locale.ENGLISH))
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0f48dc49b..b19a964e5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
-
+
+
@@ -48,7 +48,7 @@
-
+
@@ -71,29 +71,28 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -129,6 +128,7 @@
+
diff --git a/app/src/main/assets/accent.kdb b/app/src/main/assets/accent.kdb
deleted file mode 100644
index b1d7eb220..000000000
Binary files a/app/src/main/assets/accent.kdb and /dev/null differ
diff --git a/app/src/main/assets/binary-key.kdb b/app/src/main/assets/binary-key.kdb
deleted file mode 100644
index 70449ba3b..000000000
Binary files a/app/src/main/assets/binary-key.kdb and /dev/null differ
diff --git a/app/src/main/assets/binary.key b/app/src/main/assets/binary.key
deleted file mode 100644
index fd3b8274d..000000000
--- a/app/src/main/assets/binary.key
+++ /dev/null
@@ -1,2 +0,0 @@
-v7gx"Dm]tIWRPgy/˰1XfW[F%\up4
--t;z
\ No newline at end of file
diff --git a/app/src/main/assets/delete.kdb b/app/src/main/assets/delete.kdb
deleted file mode 100644
index 876547bc5..000000000
Binary files a/app/src/main/assets/delete.kdb and /dev/null differ
diff --git a/app/src/main/assets/kdb_with_xml_keyfile.kdb b/app/src/main/assets/kdb_with_xml_keyfile.kdb
deleted file mode 100644
index 87cc24e5b..000000000
Binary files a/app/src/main/assets/kdb_with_xml_keyfile.kdb and /dev/null differ
diff --git a/app/src/main/assets/key-only.kdbx b/app/src/main/assets/key-only.kdbx
deleted file mode 100644
index d65546696..000000000
Binary files a/app/src/main/assets/key-only.kdbx and /dev/null differ
diff --git a/app/src/main/assets/keyfile-binary.kdbx b/app/src/main/assets/keyfile-binary.kdbx
deleted file mode 100644
index 0bd15e95b..000000000
Binary files a/app/src/main/assets/keyfile-binary.kdbx and /dev/null differ
diff --git a/app/src/main/assets/keyfile.kdbx b/app/src/main/assets/keyfile.kdbx
deleted file mode 100644
index da16d8e44..000000000
Binary files a/app/src/main/assets/keyfile.kdbx and /dev/null differ
diff --git a/app/src/main/assets/keyfile.key b/app/src/main/assets/keyfile.key
deleted file mode 100644
index 9b07baffd..000000000
--- a/app/src/main/assets/keyfile.key
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- 1.00
-
-
- zaTWphVNtRbspnwkqjy8FGTy5IqCUx9+FNb5H+VdB24=
-
-
diff --git a/app/src/main/assets/no-encrypt.kdbx b/app/src/main/assets/no-encrypt.kdbx
deleted file mode 100644
index 88b341b83..000000000
Binary files a/app/src/main/assets/no-encrypt.kdbx and /dev/null differ
diff --git a/app/src/main/assets/test-kdbxv4.kdbx b/app/src/main/assets/test-kdbxv4.kdbx
deleted file mode 100644
index 092d9beeb..000000000
Binary files a/app/src/main/assets/test-kdbxv4.kdbx and /dev/null differ
diff --git a/app/src/main/assets/test.kdbx b/app/src/main/assets/test.kdbx
deleted file mode 100644
index 0ac33d1c5..000000000
Binary files a/app/src/main/assets/test.kdbx and /dev/null differ
diff --git a/app/src/main/assets/test1.kdb b/app/src/main/assets/test1.kdb
deleted file mode 100644
index 390ca3255..000000000
Binary files a/app/src/main/assets/test1.kdb and /dev/null differ
diff --git a/app/src/main/assets/twofish.kdb b/app/src/main/assets/twofish.kdb
deleted file mode 100644
index fb08f0466..000000000
Binary files a/app/src/main/assets/twofish.kdb and /dev/null differ
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 c4fb057c2..8ca652327 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.activities
import android.content.pm.PackageManager.NameNotFoundException
import android.os.Bundle
-import android.support.v7.widget.Toolbar
+import androidx.appcompat.widget.Toolbar
import android.util.Log
import android.view.MenuItem
import android.widget.TextView
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 ec226e9a5..6a54c6b1d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt
@@ -19,15 +19,13 @@
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.design.widget.CollapsingToolbarLayout
-import android.support.v7.app.AlertDialog
-import android.support.v7.widget.Toolbar
+import com.google.android.material.appbar.CollapsingToolbarLayout
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.widget.Toolbar
import android.util.Log
import android.view.Menu
import android.view.MenuItem
@@ -45,12 +43,11 @@ import com.kunzisoft.keepass.icons.assignDatabaseIcon
import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
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.utils.UriUtil
import com.kunzisoft.keepass.view.EntryContentsView
class EntryActivity : LockingHideActivity() {
@@ -79,7 +76,7 @@ class EntryActivity : LockingHideActivity() {
supportActionBar?.setDisplayShowHomeEnabled(true)
val currentDatabase = Database.getInstance()
- readOnly = currentDatabase.isReadOnly || readOnly
+ mReadOnly = currentDatabase.isReadOnly || mReadOnly
mShowPassword = !PreferencesUtil.isPasswordMask(this)
@@ -101,8 +98,8 @@ class EntryActivity : LockingHideActivity() {
mEntry?.touch(modified = false, touchParents = false)
// Retrieve the textColor to tint the icon
- val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.textColorInverse))
- iconColor = taIconColor.getColor(0, Color.WHITE)
+ val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
+ iconColor = taIconColor.getColor(0, Color.BLACK)
taIconColor.recycle()
// Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set
@@ -159,65 +156,86 @@ class EntryActivity : LockingHideActivity() {
// Assign basic fields
entryContentsView?.assignUserName(entry.username)
entryContentsView?.assignUserNameCopyListener(View.OnClickListener {
+ database.startManageEntry(entry)
clipboardHelper?.timeoutCopyToClipboard(entry.username,
getString(R.string.copy_field,
getString(R.string.entry_user_name)))
+ database.stopManageEntry(entry)
})
- val allowCopyPassword = PreferencesUtil.allowCopyPasswordAndProtectedFields(this)
- entryContentsView?.assignPassword(entry.password, allowCopyPassword)
- if (allowCopyPassword) {
+ val isFirstTimeAskAllowCopyPasswordAndProtectedFields =
+ PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)
+ val allowCopyPasswordAndProtectedFields =
+ PreferencesUtil.allowCopyPasswordAndProtectedFields(this)
+
+ val showWarningClipboardDialogOnClickListener = View.OnClickListener {
+ AlertDialog.Builder(this@EntryActivity)
+ .setMessage(getString(R.string.allow_copy_password_warning) +
+ "\n\n" +
+ getString(R.string.clipboard_warning))
+ .create().apply {
+ setButton(AlertDialog.BUTTON_POSITIVE, getText(R.string.enable)) {dialog, _ ->
+ PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true)
+ dialog.dismiss()
+ fillEntryDataInContentsView(entry)
+ }
+ setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.disable)) { dialog, _ ->
+ PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false)
+ dialog.dismiss()
+ fillEntryDataInContentsView(entry)
+ }
+ show()
+ }
+ }
+
+ entryContentsView?.assignPassword(entry.password, allowCopyPasswordAndProtectedFields)
+ if (allowCopyPasswordAndProtectedFields) {
entryContentsView?.assignPasswordCopyListener(View.OnClickListener {
+ database.startManageEntry(entry)
clipboardHelper?.timeoutCopyToClipboard(entry.password,
getString(R.string.copy_field,
getString(R.string.entry_password)))
+ database.stopManageEntry(entry)
})
} 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.BUTTON_POSITIVE, getText(android.R.string.ok)
- ) { dialog, _ ->
- PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true)
- dialog.dismiss()
- fillEntryDataInContentsView(entry)
- }
- warningDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel)
- ) { dialog, _ ->
- PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false)
- dialog.dismiss()
- fillEntryDataInContentsView(entry)
- }
- warningDialog.show()
- })
+ if (isFirstTimeAskAllowCopyPasswordAndProtectedFields) {
+ entryContentsView?.assignPasswordCopyListener(showWarningClipboardDialogOnClickListener)
} else {
entryContentsView?.assignPasswordCopyListener(null)
}
}
entryContentsView?.assignURL(entry.url)
- entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
entryContentsView?.assignComment(entry.notes)
// Assign custom fields
- if (entry.allowExtraFields()) {
+ if (entry.allowCustomFields()) {
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)
- )
- })
+ for (element in entry.customFields.entries) {
+ val label = element.key
+ val value = element.value
+
+ val allowCopyProtectedField = !value.isProtected || allowCopyPasswordAndProtectedFields
+ if (allowCopyProtectedField) {
+ entryContentsView?.addExtraField(label, value, allowCopyProtectedField, View.OnClickListener {
+ clipboardHelper?.timeoutCopyToClipboard(
+ value.toString(),
+ getString(R.string.copy_field, label)
+ )
+ })
+ } else {
+ // If dialog not already shown
+ if (isFirstTimeAskAllowCopyPasswordAndProtectedFields) {
+ entryContentsView?.addExtraField(label, value, allowCopyProtectedField, showWarningClipboardDialogOnClickListener)
+ } else {
+ entryContentsView?.addExtraField(label, value, allowCopyProtectedField, null)
+ }
+ }
}
}
+ entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
// Assign dates
entry.creationTime.date?.let {
@@ -271,7 +289,7 @@ class EntryActivity : LockingHideActivity() {
inflater.inflate(R.menu.entry, menu)
inflater.inflate(R.menu.database_lock, menu)
- if (readOnly) {
+ if (mReadOnly) {
menu.findItem(R.id.menu_edit)?.isVisible = false
}
@@ -306,7 +324,7 @@ class EntryActivity : LockingHideActivity() {
private fun performedNextEducation(entryActivityEducation: EntryActivityEducation,
menu: Menu) {
- if (entryContentsView?.isUserNamePresent == true
+ val entryCopyEducationPerformed = entryContentsView?.isUserNamePresent == true
&& entryActivityEducation.checkAndPerformedEntryCopyEducation(
findViewById(R.id.entry_user_name_action_image),
{
@@ -317,23 +335,29 @@ class EntryActivity : LockingHideActivity() {
{
// 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))))
- }))
- ;
+ })
+
+ if (!entryCopyEducationPerformed) {
+ // entryEditEducationPerformed
+ 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,
+ UriUtil.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_contribute -> {
+ MenuUtil.onContributionItemSelected(this)
+ return true
+ }
R.id.menu_toggle_pass -> {
mShowPassword = !mShowPassword
@@ -357,11 +381,7 @@ class EntryActivity : LockingHideActivity() {
url = "http://$url"
}
- try {
- Util.gotoUrl(this, url)
- } catch (e: ActivityNotFoundException) {
- Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show()
- }
+ UriUtil.gotoUrl(this, url)
return true
}
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 5e6dbe8bc..234cef43b 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt
@@ -22,7 +22,7 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.Handler
-import android.support.v7.widget.Toolbar
+import androidx.appcompat.widget.Toolbar
import android.util.Log
import android.view.Menu
import android.view.MenuItem
@@ -45,7 +45,9 @@ import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.view.EntryEditContentsView
-class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPickerListener, GeneratePasswordDialogFragment.GeneratePasswordListener {
+class EntryEditActivity : LockingHideActivity(),
+ IconPickerDialogFragment.IconPickerListener,
+ GeneratePasswordDialogFragment.GeneratePasswordListener {
private var mDatabase: Database? = null
@@ -150,37 +152,10 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
saveView = findViewById(R.id.entry_edit_save)
saveView?.setOnClickListener { saveEntry() }
- entryEditContentsView?.allowCustomField(mNewEntry?.allowExtraFields() == true) { addNewCustomField() }
+ entryEditContentsView?.allowCustomField(mNewEntry?.allowCustomFields() == true) { addNewCustomField() }
// Verify the education views
entryEditActivityEducation = EntryEditActivityEducation(this)
- entryEditActivityEducation?.let {
- Handler().post { performedNextEducation(it) }
- }
- }
-
- private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) {
- val passwordView = entryEditContentsView?.generatePasswordView
- val addNewFieldView = entryEditContentsView?.addNewFieldView
-
- if (passwordView != null
- && entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation(
- passwordView,
- {
- openPasswordGenerator()
- },
- {
- performedNextEducation(entryEditActivityEducation)
- }
- ))
- else if (mNewEntry != null && mNewEntry!!.allowExtraFields() && !mNewEntry!!.containsCustomFields()
- && addNewFieldView != null && addNewFieldView.visibility == View.VISIBLE
- && entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
- addNewFieldView,
- {
- addNewCustomField()
- }))
- ;
}
private fun populateViewsWithEntry(newEntry: EntryVersioned) {
@@ -197,8 +172,8 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
url = newEntry.url
password = newEntry.password
notes = newEntry.notes
- newEntry.fields.doActionToAllCustomProtectedField { key, value ->
- addNewCustomField(key, value)
+ for (entry in newEntry.customFields.entries) {
+ addNewCustomField(entry.key, entry.value)
}
}
}
@@ -281,7 +256,7 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
newEntry,
parent,
afterActionNodeFinishRunnable,
- !readOnly)
+ !mReadOnly)
}
} else {
@@ -291,7 +266,7 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
oldEntry,
newEntry,
afterActionNodeFinishRunnable,
- !readOnly)
+ !mReadOnly)
}
}
actionRunnable?.let { runnable ->
@@ -309,9 +284,39 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
inflater.inflate(R.menu.database_lock, menu)
MenuUtil.contributionMenuInflater(inflater, menu)
+ entryEditActivityEducation?.let {
+ Handler().post { performedNextEducation(it) }
+ }
+
return true
}
+ private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) {
+ val passwordView = entryEditContentsView?.generatePasswordView
+ val addNewFieldView = entryEditContentsView?.addNewFieldView
+
+ val generatePasswordEducationPerformed = passwordView != null
+ && entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation(
+ passwordView,
+ {
+ openPasswordGenerator()
+ },
+ {
+ performedNextEducation(entryEditActivityEducation)
+ }
+ )
+ if (!generatePasswordEducationPerformed) {
+ // entryNewFieldEducationPerformed
+ mNewEntry != null && mNewEntry!!.allowCustomFields() && mNewEntry!!.customFields.isEmpty()
+ && addNewFieldView != null && addNewFieldView.visibility == View.VISIBLE
+ && entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
+ addNewFieldView,
+ {
+ addNewCustomField()
+ })
+ }
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_lock -> {
@@ -319,7 +324,10 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
return true
}
- R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this)
+ R.id.menu_contribute -> {
+ MenuUtil.onContributionItemSelected(this)
+ return true
+ }
android.R.id.home -> finish()
}
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 8d957b4b8..70ef64046 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
@@ -19,7 +19,7 @@
*/
package com.kunzisoft.keepass.activities
-import android.Manifest
+import android.annotation.SuppressLint
import android.app.Activity
import android.app.assist.AssistStructure
import android.content.Intent
@@ -29,56 +29,42 @@ 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 androidx.annotation.RequiresApi
+import com.google.android.material.snackbar.Snackbar
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.SimpleItemAnimator
+import androidx.appcompat.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.dialogs.AssignMasterKeyDialogFragment
-import com.kunzisoft.keepass.activities.dialogs.CreateFileDialogFragment
-import com.kunzisoft.keepass.activities.dialogs.FileInformationDialogFragment
+import com.kunzisoft.keepass.activities.dialogs.BrowserDialogFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
-import com.kunzisoft.keepass.activities.helpers.KeyFileHelper
+import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
+import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
-import com.kunzisoft.keepass.fileselect.DeleteFileHistoryAsyncTask
-import com.kunzisoft.keepass.fileselect.FileDatabaseModel
-import com.kunzisoft.keepass.fileselect.OpenFileHistoryAsyncTask
-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.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
+import com.kunzisoft.keepass.view.asError
+import kotlinx.android.synthetic.main.activity_file_selection.*
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 {
+ AssignMasterKeyDialogFragment.AssignPasswordDialogListener {
// Views
private var fileListContainer: View? = null
@@ -92,18 +78,18 @@ class FileDatabaseSelectActivity : StylishActivity(),
// Adapter to manage database history list
private var mAdapterDatabaseHistory: FileDatabaseHistoryAdapter? = null
- private var mFileDatabaseHistory: FileDatabaseHistory? = null
+ private var mFileDatabaseHistoryAction: FileDatabaseHistoryAction? = null
private var mDatabaseFileUri: Uri? = null
- private var mKeyFileHelper: KeyFileHelper? = null
+ private var mOpenFileHelper: OpenFileHelper? = null
private var mDefaultPath: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- mFileDatabaseHistory = FileDatabaseHistory.getInstance(WeakReference(applicationContext))
+ mFileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(applicationContext)
setContentView(R.layout.activity_file_selection)
fileListContainer = findViewById(R.id.container_file_list)
@@ -131,10 +117,6 @@ class FileDatabaseSelectActivity : StylishActivity(),
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 { _ ->
@@ -143,101 +125,121 @@ class FileDatabaseSelectActivity : StylishActivity(),
if (fileName.isEmpty())
fileName = it
}
- launchPasswordActivityWithPath(fileName)
+ UriUtil.parse(fileName)?.let { fileNameUri ->
+ launchPasswordActivityWithPath(fileNameUri)
+ } ?: run {
+ Log.e(TAG, "Unable to open the database link")
+ Snackbar.make(activity_file_selection_coordinator_layout, getString(R.string.error_can_not_handle_uri), Snackbar.LENGTH_LONG).asError().show()
+ null
+ }
}
// Create button
createButtonView = findViewById(R.id.create_database)
- createButtonView?.setOnClickListener { openCreateFileDialogFragmentWithPermissionCheck() }
+ if (Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "application/x-keepass"
+ }.resolveActivity(packageManager) == null) {
+ // No Activity found that can handle this intent.
+ createButtonView?.visibility = View.GONE
+ }
+ else{
+ // There is an activity which can handle this intent.
+ createButtonView?.visibility = View.VISIBLE
+ }
+
+ createButtonView?.setOnClickListener { createNewFile() }
- mKeyFileHelper = KeyFileHelper(this)
+ mOpenFileHelper = OpenFileHelper(this)
browseButtonView = findViewById(R.id.browse_button)
- browseButtonView?.setOnClickListener(mKeyFileHelper!!.getOpenFileOnClickViewListener {
- Uri.parse("file://" + openFileNameView!!.text.toString())
+ browseButtonView?.setOnClickListener(mOpenFileHelper!!.getOpenFileOnClickViewListener {
+ UriUtil.parse(openFileNameView?.text?.toString())
})
+ // History list
+ val fileDatabaseHistoryRecyclerView = findViewById(R.id.file_list)
+ fileDatabaseHistoryRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
+ // Removes blinks
+ (fileDatabaseHistoryRecyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
// Construct adapter with listeners
- mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this@FileDatabaseSelectActivity,
- mFileDatabaseHistory?.databaseUriList ?: ArrayList())
- mAdapterDatabaseHistory?.setOnItemClickListener(this)
- mAdapterDatabaseHistory?.setFileSelectClearListener(this)
- mAdapterDatabaseHistory?.setFileInformationShowListener(this)
- databaseFileListView.adapter = mAdapterDatabaseHistory
+ mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this)
+ mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen ->
+ UriUtil.parse(fileDatabaseHistoryEntityToOpen.databaseUri)?.let { databaseFileUri ->
+ launchPasswordActivity(
+ databaseFileUri,
+ UriUtil.parse(fileDatabaseHistoryEntityToOpen.keyFileUri))
+ }
+ updateFileListVisibility()
+ }
+ mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete ->
+ // Remove from app database
+ mFileDatabaseHistoryAction?.deleteFileDatabaseHistory(fileDatabaseHistoryToDelete) { fileHistoryDeleted ->
+ // Remove from adapter
+ fileHistoryDeleted?.let { databaseFileHistoryDeleted ->
+ mAdapterDatabaseHistory?.deleteDatabaseFileHistory(databaseFileHistoryDeleted)
+ mAdapterDatabaseHistory?.notifyDataSetChanged()
+ updateFileListVisibility()
+ }
+ }
+ true
+ }
+ mAdapterDatabaseHistory?.setOnSaveAliasListener { fileDatabaseHistoryWithNewAlias ->
+ mFileDatabaseHistoryAction?.addOrUpdateFileDatabaseHistory(fileDatabaseHistoryWithNewAlias)
+ }
+ fileDatabaseHistoryRecyclerView.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 != null && fileName.isNotEmpty()) {
- val dbUri = UriUtil.parseUriFile(fileName)
- var scheme: String? = null
- if (dbUri != null)
- scheme = dbUri.scheme
-
- if (scheme != null && scheme.isNotEmpty() && scheme.equals("file", ignoreCase = true)) {
- val path = dbUri!!.path
- val db = File(path!!)
+ val databasePath = prefs.getString(PasswordActivity.KEY_DEFAULT_DATABASE_PATH, "")
- if (db.exists()) {
- launchPasswordActivityWithPath(path)
- }
- } else {
- if (dbUri != null)
- launchPasswordActivityWithPath(dbUri.toString())
- }
+ UriUtil.parse(databasePath)?.let { databaseFileUri ->
+ launchPasswordActivityWithPath(databaseFileUri)
+ } ?: run {
+ Log.i(TAG, "Unable to launch Password Activity")
}
}
- Handler().post { performedNextEducation(FileDatabaseSelectActivityEducation(this)) }
+ // Retrieve the database URI provided by file manager after an orientation change
+ if (savedInstanceState != null
+ && savedInstanceState.containsKey(EXTRA_DATABASE_URI)) {
+ mDatabaseFileUri = savedInstanceState.getParcelable(EXTRA_DATABASE_URI)
+ }
}
- 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 ->
- tapTargetView?.let {
- mKeyFileHelper?.openFileOnClickViewListener?.onClick(it)
- }
- },
- {
- fileSelectExpandableButtonView?.let {
- fileDatabaseSelectActivityEducation
- .checkAndPerformedOpenLinkDatabaseEducation(it)
- }
- }
- ))
- ;
+ /**
+ * Create a new file by calling the content provider
+ */
+ @SuppressLint("InlinedApi")
+ private fun createNewFile() {
+ try {
+ startActivityForResult(Intent(
+ Intent.ACTION_CREATE_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "application/x-keepass"
+ putExtra(Intent.EXTRA_TITLE, getString(R.string.database_file_name_default) +
+ getString(R.string.database_file_extension_default))
+ },
+ CREATE_FILE_REQUEST_CODE)
+ } catch (e: Exception) {
+ BrowserDialogFragment().show(supportFragmentManager, "browserDialog")
+ }
}
private fun fileNoFoundAction(e: FileNotFoundException) {
val error = getString(R.string.file_not_found_content)
- Toast.makeText(this@FileDatabaseSelectActivity,
- error, Toast.LENGTH_LONG).show()
+ Snackbar.make(activity_file_selection_coordinator_layout, error, Snackbar.LENGTH_LONG).asError().show()
Log.e(TAG, error, e)
}
- private fun launchPasswordActivity(fileName: String, keyFile: String) {
+ private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?) {
EntrySelectionHelper.doEntrySelectionAction(intent,
{
try {
PasswordActivity.launch(this@FileDatabaseSelectActivity,
- fileName, keyFile)
+ databaseUri, keyFile)
} catch (e: FileNotFoundException) {
fileNoFoundAction(e)
}
@@ -245,7 +247,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
{
try {
PasswordActivity.launchForKeyboardResult(this@FileDatabaseSelectActivity,
- fileName, keyFile)
+ databaseUri, keyFile)
finish()
} catch (e: FileNotFoundException) {
fileNoFoundAction(e)
@@ -255,7 +257,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity,
- fileName, keyFile,
+ databaseUri, keyFile,
assistStructure)
} catch (e: FileNotFoundException) {
fileNoFoundAction(e)
@@ -265,8 +267,8 @@ class FileDatabaseSelectActivity : StylishActivity(),
})
}
- private fun launchPasswordActivityWithPath(path: String) {
- launchPasswordActivity(path, "")
+ private fun launchPasswordActivityWithPath(databaseUri: Uri) {
+ launchPasswordActivity(databaseUri, null)
// Delete flickering for kitkat <=
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
overridePendingTransition(0, 0)
@@ -295,26 +297,23 @@ class FileDatabaseSelectActivity : StylishActivity(),
super.onResume()
updateExternalStorageWarning()
- updateFileListVisibility()
- mAdapterDatabaseHistory!!.notifyDataSetChanged()
+
+ // Construct adapter with listeners
+ mFileDatabaseHistoryAction?.getAllFileDatabaseHistories { databaseFileHistoryList ->
+ databaseFileHistoryList?.let {
+ mAdapterDatabaseHistory?.addDatabaseFileHistoryList(it)
+ 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")
+ // to retrieve the URI of a created database after an orientation change
+ outState.putParcelable(EXTRA_DATABASE_URI, mDatabaseFileUri)
}
private fun updateFileListVisibility() {
@@ -324,88 +323,12 @@ class FileDatabaseSelectActivity : StylishActivity(),
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, "UTF-8")
- // 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
- if (pathFile == null)
- return false
- 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 {
- UriUtil.parseUriFile(mDatabaseFileUri)?.let { databaseUri ->
+ mDatabaseFileUri?.let { databaseUri ->
// Create the new database
ProgressDialogThread(this@FileDatabaseSelectActivity,
@@ -418,22 +341,21 @@ class FileDatabaseSelectActivity : StylishActivity(),
keyFileChecked,
keyFile,
true, // TODO get readonly
- LaunchGroupActivityFinish(databaseUri)
+ LaunchGroupActivityFinish(databaseUri, keyFile)
)
},
R.string.progress_create)
.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()
+ val error = getString(R.string.error_create_database_file)
+ Snackbar.make(activity_file_selection_coordinator_layout, error, Snackbar.LENGTH_LONG).asError().show()
+ Log.e(TAG, error, e)
}
}
- private inner class LaunchGroupActivityFinish internal constructor(private val fileURI: Uri) : ActionRunnable() {
+ private inner class LaunchGroupActivityFinish(private val databaseFileUri: Uri,
+ private val keyFileUri: Uri?) : ActionRunnable() {
override fun run() {
finishRun(true, null)
@@ -443,7 +365,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
runOnUiThread {
if (result.isSuccess) {
// Add database to recent files
- mFileDatabaseHistory?.addDatabaseUri(fileURI)
+ mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(databaseFileUri, keyFileUri)
mAdapterDatabaseHistory?.notifyDataSetChanged()
updateFileListVisibility()
GroupActivity.launch(this@FileDatabaseSelectActivity)
@@ -460,29 +382,6 @@ class FileDatabaseSelectActivity : StylishActivity(),
}
- 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({
- fileDatabaseModel.fileUri?.let {
- mFileDatabaseHistory?.deleteDatabaseUri(it)
- }
- mAdapterDatabaseHistory?.notifyDataSetChanged()
- updateFileListVisibility()
- }, mFileDatabaseHistory, mAdapterDatabaseHistory).execute(fileDatabaseModel)
- return true
- }
-
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
@@ -490,44 +389,73 @@ class FileDatabaseSelectActivity : StylishActivity(),
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
}
- mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data
+ mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
) { uri ->
if (uri != null) {
if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) {
- launchPasswordActivityWithPath(uri.toString())
+ launchPasswordActivityWithPath(uri)
} 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()
+ // Retrieve the created URI from the file manager
+ if (requestCode == CREATE_FILE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+ mDatabaseFileUri = data?.data
+ if (mDatabaseFileUri != null) {
+ AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog")
+ }
+ // else {
+ // TODO Show error
+ // }
+ }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
MenuUtil.defaultMenuInflater(menuInflater, menu)
+
+ Handler().post { performedNextEducation(FileDatabaseSelectActivityEducation(this)) }
+
return true
}
+ private fun performedNextEducation(fileDatabaseSelectActivityEducation: FileDatabaseSelectActivityEducation) {
+ // If no recent files
+ val createDatabaseEducationPerformed = createButtonView != null && createButtonView!!.visibility == View.VISIBLE
+ && mAdapterDatabaseHistory != null
+ && mAdapterDatabaseHistory!!.itemCount > 0
+ && fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation(
+ createButtonView!!,
+ {
+ createNewFile()
+ },
+ {
+ // But if the user cancel, it can also select a database
+ performedNextEducation(fileDatabaseSelectActivityEducation)
+ })
+ if (!createDatabaseEducationPerformed) {
+ // selectDatabaseEducationPerformed
+ browseButtonView != null
+ && fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation(
+ browseButtonView!!,
+ {tapTargetView ->
+ tapTargetView?.let {
+ mOpenFileHelper?.openFileOnClickViewListener?.onClick(it)
+ }
+ },
+ {
+ fileSelectExpandableButtonView?.let {
+ fileDatabaseSelectActivityEducation
+ .checkAndPerformedOpenLinkDatabaseEducation(it)
+ }
+ }
+ )
+ }
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item)
}
@@ -536,6 +464,9 @@ class FileDatabaseSelectActivity : StylishActivity(),
private const val TAG = "FileDbSelectActivity"
private const val EXTRA_STAY = "EXTRA_STAY"
+ private const val EXTRA_DATABASE_URI = "EXTRA_DATABASE_URI"
+
+ private const val CREATE_FILE_REQUEST_CODE = 3853
/*
* -------------------------
@@ -550,7 +481,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
*/
fun launchForKeyboardSelection(activity: Activity) {
- KeyboardHelper.startActivityForKeyboardSelection(activity, Intent(activity, FileDatabaseSelectActivity::class.java))
+ EntrySelectionHelper.startActivityForEntrySelection(activity, Intent(activity, FileDatabaseSelectActivity::class.java))
}
/*
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 e85733efd..1b5a29882 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt
@@ -29,11 +29,10 @@ import android.graphics.Color
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 androidx.annotation.RequiresApi
+import androidx.fragment.app.FragmentManager
+import androidx.appcompat.widget.SearchView
+import androidx.appcompat.widget.Toolbar
import android.util.Log
import android.view.Menu
import android.view.MenuItem
@@ -58,7 +57,6 @@ import com.kunzisoft.keepass.database.action.node.ActionNodeDatabaseRunnable.Com
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.education.GroupActivityEducation
import com.kunzisoft.keepass.icons.assignDatabaseIcon
-import com.kunzisoft.keepass.magikeyboard.KeyboardHelper
import com.kunzisoft.keepass.magikeyboard.MagikIME
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
@@ -178,12 +176,12 @@ class GroupActivity : LockingActivity(),
// Initialize the fragment with the list
mListNodesFragment = supportFragmentManager.findFragmentByTag(fragmentTag) as ListNodesFragment?
if (mListNodesFragment == null)
- mListNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, readOnly, mCurrentGroupIsASearch)
+ mListNodesFragment = ListNodesFragment.newInstance(mCurrentGroup, mReadOnly, mCurrentGroupIsASearch)
// Attach fragment to content view
supportFragmentManager.beginTransaction().replace(
R.id.nodes_list_fragment_container,
- mListNodesFragment,
+ mListNodesFragment!!,
fragmentTag)
.commit()
@@ -207,15 +205,19 @@ class GroupActivity : LockingActivity(),
Log.i(TAG, "Finished creating tree")
}
- override fun onNewIntent(intent: Intent) {
- Log.d(TAG, "setNewIntent: $intent")
- setIntent(intent)
- mCurrentGroupIsASearch = if (Intent.ACTION_SEARCH == intent.action) {
- // only one instance of search in backstack
- openSearchGroup(retrieveCurrentGroup(intent, null))
- true
- } else {
- false
+ override fun onNewIntent(intent: Intent?) {
+ super.onNewIntent(intent)
+
+ intent?.let { intentNotNull ->
+ Log.d(TAG, "setNewIntent: $intentNotNull")
+ setIntent(intentNotNull)
+ mCurrentGroupIsASearch = if (Intent.ACTION_SEARCH == intentNotNull.action) {
+ // only one instance of search in backstack
+ openSearchGroup(retrieveCurrentGroup(intentNotNull, null))
+ true
+ } else {
+ false
+ }
}
}
@@ -239,7 +241,7 @@ class GroupActivity : LockingActivity(),
// Check TimeoutHelper
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this) {
// Open a group in a new fragment
- val newListNodeFragment = ListNodesFragment.newInstance(group, readOnly, isASearch)
+ val newListNodeFragment = ListNodesFragment.newInstance(group, mReadOnly, isASearch)
val fragmentTransaction = supportFragmentManager.beginTransaction()
// Different animation
val fragmentTag: String
@@ -283,6 +285,9 @@ class GroupActivity : LockingActivity(),
private fun retrieveCurrentGroup(intent: Intent, savedInstanceState: Bundle?): GroupVersioned? {
+ // Force read only if the database is like that
+ mReadOnly = mDatabase?.isReadOnly == true || mReadOnly
+
// If it's a search
if (Intent.ACTION_SEARCH == intent.action) {
return mDatabase?.search(intent.getStringExtra(SearchManager.QUERY).trim { it <= ' ' })
@@ -297,8 +302,6 @@ class GroupActivity : LockingActivity(),
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) {
@@ -357,7 +360,7 @@ class GroupActivity : LockingActivity(),
}
// Show selection mode message if needed
- if (selectionMode) {
+ if (mSelectionMode) {
modeTitleView?.visibility = View.VISIBLE
} else {
modeTitleView?.visibility = View.GONE
@@ -367,8 +370,8 @@ class GroupActivity : LockingActivity(),
addNodeButtonView?.apply {
// To enable add button
- val addGroupEnabled = !readOnly && !mCurrentGroupIsASearch
- var addEntryEnabled = !readOnly && !mCurrentGroupIsASearch
+ val addGroupEnabled = !mReadOnly && !mCurrentGroupIsASearch
+ var addEntryEnabled = !mReadOnly && !mCurrentGroupIsASearch
mCurrentGroup?.let {
val isRoot = it == mRootGroup
if (!it.allowAddEntryIfIsRoot())
@@ -401,7 +404,7 @@ class GroupActivity : LockingActivity(),
val entryVersioned = node as EntryVersioned
EntrySelectionHelper.doEntrySelectionAction(intent,
{
- EntryActivity.launch(this@GroupActivity, entryVersioned, readOnly)
+ EntryActivity.launch(this@GroupActivity, entryVersioned, mReadOnly)
},
{
// Populate Magikeyboard with entry
@@ -481,7 +484,7 @@ class GroupActivity : LockingActivity(),
entryToCopy,
newParent,
AfterAddNodeRunnable(),
- !readOnly)
+ !mReadOnly)
}.start()
}
@@ -526,7 +529,7 @@ class GroupActivity : LockingActivity(),
groupToMove,
newParent,
AfterAddNodeRunnable(),
- !readOnly)
+ !mReadOnly)
}.start()
}
@@ -538,7 +541,7 @@ class GroupActivity : LockingActivity(),
entryToMove,
newParent,
AfterAddNodeRunnable(),
- !readOnly)
+ !mReadOnly)
}.start()
}
@@ -558,7 +561,7 @@ class GroupActivity : LockingActivity(),
Database.getInstance(),
group,
AfterDeleteNodeRunnable(),
- !readOnly)
+ !mReadOnly)
}.start()
}
@@ -569,7 +572,7 @@ class GroupActivity : LockingActivity(),
Database.getInstance(),
entry,
AfterDeleteNodeRunnable(),
- !readOnly)
+ !mReadOnly)
}.start()
}
@@ -586,7 +589,7 @@ class GroupActivity : LockingActivity(),
val inflater = menuInflater
inflater.inflate(R.menu.search, menu)
inflater.inflate(R.menu.database_lock, menu)
- if (!selectionMode) {
+ if (!mSelectionMode) {
inflater.inflate(R.menu.default_menu, menu)
MenuUtil.contributionMenuInflater(inflater, menu)
}
@@ -628,8 +631,9 @@ class GroupActivity : LockingActivity(),
private fun performedNextEducation(groupActivityEducation: GroupActivityEducation,
menu: Menu) {
+
// If no node, show education to add new one
- if (mListNodesFragment != null
+ val addNodeButtonEducationPerformed = mListNodesFragment != null
&& mListNodesFragment!!.isEmpty
&& addNodeButtonView?.addButtonView != null
&& addNodeButtonView!!.isEnable
@@ -641,71 +645,47 @@ class GroupActivity : LockingActivity(),
{
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(
+ )
+ if (!addNodeButtonEducationPerformed) {
+
+ val searchMenuEducationPerformed = 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)
+ })
+
+ if (!searchMenuEducationPerformed) {
+
+ val sortMenuEducationPerformed = 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)
+ if (!sortMenuEducationPerformed) {
+ // lockMenuEducationPerformed
+ 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)
+ })
+ }
+ }
}
}
@@ -724,7 +704,7 @@ class GroupActivity : LockingActivity(),
}
else -> {
// Check the time lock before launching settings
- MenuUtil.onDefaultMenuOptionsItemSelected(this, item, readOnly, true)
+ MenuUtil.onDefaultMenuOptionsItemSelected(this, item, mReadOnly, true)
return super.onOptionsItemSelected(item)
}
}
@@ -754,7 +734,7 @@ class GroupActivity : LockingActivity(),
newGroup,
currentGroup,
AfterAddNodeRunnable(),
- !readOnly)
+ !mReadOnly)
}.start()
}
}
@@ -776,7 +756,7 @@ class GroupActivity : LockingActivity(),
oldGroupToUpdate,
updateGroup,
AfterUpdateNodeRunnable(),
- !readOnly)
+ !mReadOnly)
}.start()
}
}
@@ -857,11 +837,9 @@ class GroupActivity : LockingActivity(),
}
private fun showWarnings() {
- // TODO Preferences
if (Database.getInstance().isReadOnly) {
- val prefs = PreferenceManager.getDefaultSharedPreferences(this)
- if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) {
- ReadOnlyDialog(this).show()
+ if (PreferencesUtil.showReadOnlyWarning(this)) {
+ ReadOnlyDialog().show(supportFragmentManager, "readOnlyDialog")
}
}
}
@@ -870,18 +848,25 @@ class GroupActivity : LockingActivity(),
mListNodesFragment?.onSortSelected(sortNodeEnum, ascending, groupsBefore, recycleBinBottom)
}
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
+ override fun startActivity(intent: Intent) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
- }
+ // Get the intent, verify the action and get the query
+ if (Intent.ACTION_SEARCH == intent.action) {
+ // manually launch the real search activity
+ val searchIntent = Intent(applicationContext, GroupActivity::class.java).apply {
+ // Add bundle of current intent
+ putExtras(this@GroupActivity.intent)
+ // add query to the Intent Extras
+ action = Intent.ACTION_SEARCH
+ putExtra(SearchManager.QUERY, intent.getStringExtra(SearchManager.QUERY))
+ }
- // Not directly get the entry from intent data but from database
- mListNodesFragment?.rebuildList()
+ super.startActivity(searchIntent)
+ } else {
+ super.startActivity(intent)
+ }
}
- @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
@@ -894,9 +879,18 @@ class GroupActivity : LockingActivity(),
intent.flags = flags
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- super.startActivityForResult(intent, requestCode, options)
+ super.startActivityForResult(intent, requestCode, options)
+ }
+
+ 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
+ mListNodesFragment?.rebuildList()
}
private fun removeSearchInIntent(intent: Intent) {
@@ -973,10 +967,10 @@ class GroupActivity : LockingActivity(),
*/
// TODO implement pre search to directly open the direct group
- fun launchForKeyboardSelection(activity: Activity, readOnly: Boolean) {
+ fun launchForKeyboarSelection(activity: Activity, readOnly: Boolean) {
TimeoutHelper.recordTime(activity)
buildAndLaunchIntent(activity, null, readOnly) { intent ->
- KeyboardHelper.startActivityForKeyboardSelection(activity, intent)
+ EntrySelectionHelper.startActivityForEntrySelection(activity, intent)
}
}
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 618f3bb0b..3346deb1f 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt
@@ -5,8 +5,8 @@ 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 androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
@@ -53,31 +53,31 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
val isEmpty: Boolean
get() = mAdapter == null || mAdapter?.itemCount?:0 <= 0
- override fun onAttach(context: Context?) {
+ override fun onAttach(context: Context) {
super.onAttach(context)
try {
- nodeClickCallback = context as NodeAdapter.NodeClickCallback?
+ nodeClickCallback = context as NodeAdapter.NodeClickCallback
} catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception
- throw ClassCastException(context?.toString()
+ throw ClassCastException(context.toString()
+ " must implement " + NodeAdapter.NodeClickCallback::class.java.name)
}
try {
- nodeMenuListener = context as NodeAdapter.NodeMenuListener?
+ nodeMenuListener = context as NodeAdapter.NodeMenuListener
} catch (e: ClassCastException) {
nodeMenuListener = null
// Context menu can be omit
- Log.w(TAG, context?.toString()
+ Log.w(TAG, context.toString()
+ " must implement " + NodeAdapter.NodeMenuListener::class.java.name)
}
try {
- onScrollListener = context as OnScrollListener?
+ onScrollListener = context as OnScrollListener
} catch (e: ClassCastException) {
onScrollListener = null
// Context menu can be omit
- Log.w(TAG, context?.toString()
+ Log.w(TAG, context.toString()
+ " must implement " + RecyclerView.OnScrollListener::class.java.name)
}
}
@@ -130,7 +130,7 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
onScrollListener?.let { onScrollListener ->
listView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
- override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
onScrollListener.onScrolled(dy)
}
@@ -195,14 +195,14 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
}
}
- override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
- inflater?.inflate(R.menu.tree, menu)
+ 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) {
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
R.id.menu_sort -> {
context?.let { context ->
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 638e99320..1df82893d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt
@@ -19,56 +19,59 @@
*/
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 androidx.annotation.RequiresApi
+import com.google.android.material.snackbar.Snackbar
+import androidx.appcompat.widget.Toolbar
import android.text.Editable
import android.text.TextWatcher
+import android.util.Log
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
import android.widget.*
+import androidx.biometric.BiometricManager
import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.activities.dialogs.FingerPrintExplanationDialog
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
-import com.kunzisoft.keepass.activities.helpers.*
+import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
+import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
+import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.stylish.StylishActivity
+import com.kunzisoft.keepass.app.database.CipherDatabaseAction
+import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
+import com.kunzisoft.keepass.utils.FileDatabaseInfo
+import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.PasswordActivityEducation
-import com.kunzisoft.keepass.fingerprint.FingerPrintHelper
-import com.kunzisoft.keepass.fingerprint.FingerPrintViewsManager
-import com.kunzisoft.keepass.magikeyboard.KeyboardHelper
+import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
-import com.kunzisoft.keepass.view.FingerPrintInfoView
-import permissions.dispatcher.*
-import java.io.File
+import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
+import com.kunzisoft.keepass.view.asError
+import kotlinx.android.synthetic.main.activity_password.*
import java.io.FileNotFoundException
import java.lang.ref.WeakReference
-@RuntimePermissions
-class PasswordActivity : StylishActivity(),
- UriIntentInitTaskCallback {
+class PasswordActivity : StylishActivity() {
// Views
private var toolbar: Toolbar? = null
@@ -80,18 +83,18 @@ class PasswordActivity : StylishActivity(),
private var checkboxPasswordView: CompoundButton? = null
private var checkboxKeyFileView: CompoundButton? = null
private var checkboxDefaultDatabaseView: CompoundButton? = null
- private var fingerPrintInfoView: FingerPrintInfoView? = null
+ private var advancedUnlockInfoView: AdvancedUnlockInfoView? = null
private var enableButtonOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
private var mDatabaseFileUri: Uri? = null
private var prefs: SharedPreferences? = null
private var mRememberKeyFile: Boolean = false
- private var mKeyFileHelper: KeyFileHelper? = null
+ private var mOpenFileHelper: OpenFileHelper? = null
private var readOnly: Boolean = false
- private var fingerPrintViewsManager: FingerPrintViewsManager? = null
+ private var advancedUnlockedManager: AdvancedUnlockedManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -116,13 +119,13 @@ class PasswordActivity : StylishActivity(),
checkboxPasswordView = findViewById(R.id.password_checkbox)
checkboxKeyFileView = findViewById(R.id.keyfile_checkox)
checkboxDefaultDatabaseView = findViewById(R.id.default_database)
- fingerPrintInfoView = findViewById(R.id.fingerprint_info)
+ advancedUnlockInfoView = findViewById(R.id.fingerprint_info)
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState)
val browseView = findViewById(R.id.browse_button)
- mKeyFileHelper = KeyFileHelper(this@PasswordActivity)
- browseView.setOnClickListener(mKeyFileHelper!!.openFileOnClickViewListener)
+ mOpenFileHelper = OpenFileHelper(this@PasswordActivity)
+ browseView.setOnClickListener(mOpenFileHelper!!.openFileOnClickViewListener)
passwordView?.setOnEditorActionListener(onEditorActionListener)
passwordView?.addTextChangedListener(object : TextWatcher {
@@ -172,8 +175,7 @@ class PasswordActivity : StylishActivity(),
// For check shutdown
super.onResume()
- UriIntentInitTask(WeakReference(this), this, mRememberKeyFile)
- .execute(intent)
+ initUriFromIntent()
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -181,26 +183,51 @@ class PasswordActivity : StylishActivity(),
super.onSaveInstanceState(outState)
}
- override fun onPostInitTask(databaseFileUri: Uri?, keyFileUri: Uri?, errorStringId: Int?) {
- mDatabaseFileUri = databaseFileUri
+ private fun initUriFromIntent() {
+
+ val databaseUri: Uri?
+ val keyFileUri: Uri?
+
+ // If is a view intent
+ val action = intent.action
+ if (action != null && action == VIEW_INTENT) {
+
+ val databaseUriRetrieve = intent.data
+ // Stop activity here if we can't verify database URI
+ if (!UriUtil.verifyFileUri(databaseUriRetrieve)) {
+ Log.e(TAG, "File URI not validate")
+ finish()
+ }
+ databaseUri = databaseUriRetrieve
+ keyFileUri = UriUtil.getUriFromIntent(intent, KEY_KEYFILE)
+
+ } else {
+ databaseUri = intent.getParcelableExtra(KEY_FILENAME)
+ keyFileUri = intent.getParcelableExtra(KEY_KEYFILE)
+ }
- if (errorStringId != null) {
- Toast.makeText(this@PasswordActivity, errorStringId, Toast.LENGTH_LONG).show()
- finish()
- return
+ // Post init uri with KeyFile if needed
+ if (mRememberKeyFile && (keyFileUri == null || keyFileUri.toString().isEmpty())) {
+ // Retrieve KeyFile in a thread
+ databaseUri?.let { databaseUriNotNull ->
+ FileDatabaseHistoryAction.getInstance(applicationContext)
+ .getKeyFileUriByDatabaseUri(databaseUriNotNull) {
+ onPostInitUri(databaseUri, it)
+ }
+ }
+ } else {
+ onPostInitUri(databaseUri, keyFileUri)
}
+ }
- // Verify permission to read file
- if (databaseFileUri != null && !databaseFileUri.scheme!!.contains("content"))
- doNothingWithPermissionCheck()
+ private fun onPostInitUri(databaseFileUri: Uri?, keyFileUri: Uri?) {
+ mDatabaseFileUri = databaseFileUri
// Define title
- val dbUriString = databaseFileUri?.toString() ?: ""
- if (dbUriString.isNotEmpty()) {
- if (PreferencesUtil.isFullFilePathEnable(this))
- filenameView?.text = dbUriString
- else
- filenameView?.text = File(databaseFileUri!!.path!!).name // TODO Encapsulate
+ databaseFileUri?.let {
+ FileDatabaseInfo(this, it).retrieveDatabaseTitle { title ->
+ filenameView?.text = title
+ }
}
// Define Key File text
@@ -211,14 +238,16 @@ class PasswordActivity : StylishActivity(),
// Define listeners for default database checkbox and validate button
checkboxDefaultDatabaseView?.setOnCheckedChangeListener { _, isChecked ->
- var newDefaultFileName = ""
+ var newDefaultFileName: Uri? = null
if (isChecked) {
- newDefaultFileName = databaseFileUri?.toString() ?: newDefaultFileName
+ newDefaultFileName = databaseFileUri ?: newDefaultFileName
}
- prefs?.edit()?.apply() {
- putString(KEY_DEFAULT_FILENAME, newDefaultFileName)
- apply()
+ newDefaultFileName?.let {
+ prefs?.edit()?.apply {
+ putString(KEY_DEFAULT_DATABASE_PATH, newDefaultFileName.toString())
+ apply()
+ }
}
val backupManager = BackupManager(this@PasswordActivity)
@@ -227,10 +256,10 @@ class PasswordActivity : StylishActivity(),
confirmButtonView?.setOnClickListener { verifyCheckboxesAndLoadDatabase() }
// Retrieve settings for default database
- val defaultFilename = prefs?.getString(KEY_DEFAULT_FILENAME, "")
+ val defaultFilename = prefs?.getString(KEY_DEFAULT_DATABASE_PATH, "")
if (databaseFileUri != null
&& databaseFileUri.path != null && databaseFileUri.path!!.isNotEmpty()
- && databaseFileUri == UriUtil.parseUriFile(defaultFilename)) {
+ && databaseFileUri == UriUtil.parse(defaultFilename)) {
checkboxDefaultDatabaseView?.isChecked = true
}
@@ -247,30 +276,42 @@ class PasswordActivity : StylishActivity(),
// Init FingerPrint elements
var fingerPrintInit = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (PreferencesUtil.isFingerprintEnable(this)) {
- if (fingerPrintViewsManager == null) {
- fingerPrintViewsManager = FingerPrintViewsManager(this,
+ if (PreferencesUtil.isBiometricUnlockEnable(this)) {
+
+ advancedUnlockInfoView?.setOnClickListener {
+ FingerPrintExplanationDialog().show(supportFragmentManager, "fingerPrintExplanationDialog")
+ }
+
+ if (advancedUnlockedManager == null && databaseFileUri != null) {
+ advancedUnlockedManager = AdvancedUnlockedManager(this,
databaseFileUri,
- fingerPrintInfoView,
+ advancedUnlockInfoView,
checkboxPasswordView,
enableButtonOnCheckedChangeListener,
- passwordView) { passwordRetrieve ->
- // Load the database if password is registered or retrieve
- passwordRetrieve?.let {
+ passwordView,
+ { passwordEncrypted, ivSpec ->
+ // Load the database if password is registered with biometric
+ if (passwordEncrypted != null && ivSpec != null) {
+ verifyCheckboxesAndLoadDatabase(
+ CipherDatabaseEntity(
+ databaseFileUri.toString(),
+ passwordEncrypted,
+ ivSpec)
+ )
+ }
+ },
+ { passwordDecrypted ->
+ // Load the database if password is retrieve from biometric
+ passwordDecrypted?.let {
// Retrieve from fingerprint
verifyKeyFileCheckboxAndLoadDatabase(it)
- } ?: run {
- // Register with fingerprint
- verifyCheckboxesAndLoadDatabase()
}
- }
+ })
}
- fingerPrintViewsManager?.initFingerprint()
- // checks if fingerprint is available, will also start listening for fingerprints when available
- fingerPrintViewsManager?.checkFingerprintAvailability()
+ advancedUnlockedManager?.initBiometric()
fingerPrintInit = true
} else {
- fingerPrintViewsManager?.destroy()
+ advancedUnlockedManager?.destroy()
}
}
if (!fingerPrintInit) {
@@ -328,27 +369,34 @@ class PasswordActivity : StylishActivity(),
override fun onPause() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- fingerPrintViewsManager?.stopListening()
+ advancedUnlockedManager?.pause()
}
super.onPause()
}
override fun onDestroy() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- fingerPrintViewsManager?.destroy()
+ advancedUnlockedManager?.destroy()
}
super.onDestroy()
}
- private fun verifyCheckboxesAndLoadDatabase(password: String? = passwordView?.text?.toString(),
- keyFile: Uri? = UriUtil.parseUriFile(keyFileView?.text?.toString())) {
+ private fun verifyCheckboxesAndLoadDatabase(cipherDatabaseEntity: CipherDatabaseEntity? = null) {
+ val password: String? = passwordView?.text?.toString()
+ val keyFile: Uri? = UriUtil.parse(keyFileView?.text?.toString())
+ verifyCheckboxesAndLoadDatabase(password, keyFile, cipherDatabaseEntity)
+ }
+
+ private fun verifyCheckboxesAndLoadDatabase(password: String?,
+ keyFile: Uri?,
+ cipherDatabaseEntity: CipherDatabaseEntity? = null) {
val keyPassword = if (checkboxPasswordView?.isChecked != true) null else password
val keyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
- loadDatabase(keyPassword, keyFileUri)
+ loadDatabase(keyPassword, keyFileUri, cipherDatabaseEntity)
}
- private fun verifyKeyFileCheckboxAndLoadDatabase(password: String? = passwordView?.text?.toString(),
- keyFile: Uri? = UriUtil.parseUriFile(keyFileView?.text?.toString())) {
+ private fun verifyKeyFileCheckboxAndLoadDatabase(password: String?) {
+ val keyFile: Uri? = UriUtil.parse(keyFileView?.text?.toString())
val keyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
loadDatabase(password, keyFileUri)
}
@@ -358,10 +406,12 @@ class PasswordActivity : StylishActivity(),
checkboxPasswordView?.isChecked = false
}
- private fun loadDatabase(password: String?, keyFile: Uri?) {
+ private fun loadDatabase(password: String?, keyFile: Uri?, cipherDatabaseEntity: CipherDatabaseEntity? = null) {
- if (PreferencesUtil.deletePasswordAfterConnexionAttempt(this)) {
- removePassword()
+ runOnUiThread {
+ if (PreferencesUtil.deletePasswordAfterConnexionAttempt(this)) {
+ removePassword()
+ }
}
// Clear before we load
@@ -379,7 +429,7 @@ class PasswordActivity : StylishActivity(),
password,
keyFile,
progressTaskUpdater,
- AfterLoadingDatabase(database, password))
+ AfterLoadingDatabase(database, password, cipherDatabaseEntity))
},
R.string.loading_database).start()
}
@@ -388,17 +438,17 @@ class PasswordActivity : StylishActivity(),
/**
* Called after verify and try to opening the database
*/
- private inner class AfterLoadingDatabase internal constructor(var database: Database,
- val password: String?)
+ private inner class AfterLoadingDatabase(val database: Database, val password: String?,
+ val cipherDatabaseEntity: CipherDatabaseEntity? = null)
: ActionRunnable() {
override fun onFinishRun(result: Result) {
runOnUiThread {
// Recheck fingerprint if error
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (PreferencesUtil.isFingerprintEnable(this@PasswordActivity)) {
- // Stay with the same mode
- fingerPrintViewsManager?.reInitWithFingerprintMode()
+ if (PreferencesUtil.isBiometricUnlockEnable(this@PasswordActivity)) {
+ // Stay with the same mode and init it
+ advancedUnlockedManager?.initBiometricMode()
}
}
@@ -406,32 +456,45 @@ class PasswordActivity : StylishActivity(),
// Remove the password in view in all cases
removePassword()
- if (database.validatePasswordEncoding(password)) {
- launchGroupActivity()
+ // Register the biometric
+ if (cipherDatabaseEntity != null) {
+ CipherDatabaseAction.getInstance(this@PasswordActivity)
+ .addOrUpdateCipherDatabase(cipherDatabaseEntity) {
+ checkAndLaunchGroupActivity(database, password)
+ }
} else {
- PasswordEncodingDialogFragment().apply {
- positiveButtonClickListener = DialogInterface.OnClickListener { _, _ ->
- launchGroupActivity()
- }
- show(supportFragmentManager, "passwordEncodingTag")
- }
+ checkAndLaunchGroupActivity(database, password)
}
+
} else {
if (result.message != null && result.message!!.isNotEmpty()) {
- Toast.makeText(this@PasswordActivity, result.message, Toast.LENGTH_LONG).show()
+ Snackbar.make(activity_password_coordinator_layout, result.message!!, Snackbar.LENGTH_LONG).asError().show()
}
}
}
}
}
+ private fun checkAndLaunchGroupActivity(database: Database, password: String?) {
+ if (database.validatePasswordEncoding(password)) {
+ launchGroupActivity()
+ } else {
+ PasswordEncodingDialogFragment().apply {
+ positiveButtonClickListener = DialogInterface.OnClickListener { _, _ ->
+ launchGroupActivity()
+ }
+ show(supportFragmentManager, "passwordEncodingTag")
+ }
+ }
+ }
+
private fun launchGroupActivity() {
EntrySelectionHelper.doEntrySelectionAction(intent,
{
GroupActivity.launch(this@PasswordActivity, readOnly)
},
{
- GroupActivity.launchForKeyboardSelection(this@PasswordActivity, readOnly)
+ GroupActivity.launchForKeyboarSelection(this@PasswordActivity, readOnly)
// Do not keep history
finish()
},
@@ -452,7 +515,7 @@ class PasswordActivity : StylishActivity(),
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Fingerprint menu
- fingerPrintViewsManager?.inflateOptionsMenu(inflater, menu)
+ advancedUnlockedManager?.inflateOptionsMenu(inflater, menu)
}
super.onCreateOptionsMenu(menu)
@@ -465,32 +528,41 @@ class PasswordActivity : StylishActivity(),
private fun performedNextEducation(passwordActivityEducation: PasswordActivityEducation,
menu: Menu) {
- if (toolbar != null
- && passwordActivityEducation.checkAndPerformedFingerprintUnlockEducation(
+ val unlockEducationPerformed = toolbar != null
+ && passwordActivityEducation.checkAndPerformedUnlockEducation(
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))
- && fingerPrintInfoView != null && fingerPrintInfoView?.fingerPrintImageView != null
- && passwordActivityEducation.checkAndPerformedFingerprintEducation(fingerPrintInfoView?.fingerPrintImageView!!))
- ;
+ })
+ if (!unlockEducationPerformed) {
+
+ val readOnlyEducationPerformed = 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)
+ })
+
+ if (!readOnlyEducationPerformed) {
+
+ val biometricCanAuthenticate = BiometricManager.from(this).canAuthenticate()
+ // fingerprintEducationPerformed
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+ && PreferencesUtil.isBiometricUnlockEnable(applicationContext)
+ && (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)
+ && advancedUnlockInfoView != null && advancedUnlockInfoView?.unlockIconImageView != null
+ && passwordActivityEducation.checkAndPerformedFingerprintEducation(advancedUnlockInfoView?.unlockIconImageView!!)
+
+ }
+ }
}
private fun changeOpenFileReadIcon(togglePassword: MenuItem) {
@@ -512,7 +584,7 @@ class PasswordActivity : StylishActivity(),
changeOpenFileReadIcon(item)
}
R.id.menu_fingerprint_remove_key -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- fingerPrintViewsManager?.deleteEntryKey()
+ advancedUnlockedManager?.deleteEntryKey()
}
else -> return MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
}
@@ -520,12 +592,6 @@ class PasswordActivity : StylishActivity(),
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,
@@ -538,7 +604,7 @@ class PasswordActivity : StylishActivity(),
}
var keyFileResult = false
- mKeyFileHelper?.let {
+ mOpenFileHelper?.let {
keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data
) { uri ->
if (uri != null) {
@@ -557,64 +623,28 @@ class PasswordActivity : StylishActivity(),
}
}
- @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"
+ const val KEY_DEFAULT_DATABASE_PATH = "KEY_DEFAULT_DATABASE_PATH"
+
+ private const val KEY_FILENAME = "fileName"
+ private const val KEY_KEYFILE = "keyFile"
+ private const val VIEW_INTENT = "android.intent.action.VIEW"
private const val KEY_PASSWORD = "password"
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
- private fun buildAndLaunchIntent(activity: Activity, fileName: String, keyFile: String,
+ private fun buildAndLaunchIntent(activity: Activity, databaseFile: Uri, keyFile: Uri?,
intentBuildLauncher: (Intent) -> Unit) {
val intent = Intent(activity, PasswordActivity::class.java)
- intent.putExtra(UriIntentInitTask.KEY_FILENAME, fileName)
- intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile)
+ intent.putExtra(KEY_FILENAME, databaseFile)
+ if (keyFile != null)
+ intent.putExtra(KEY_KEYFILE, keyFile)
intentBuildLauncher.invoke(intent)
}
- @Throws(FileNotFoundException::class)
- private fun verifyFileNameUriFromLaunch(fileName: String) {
- if (fileName.isEmpty()) {
- throw FileNotFoundException()
- }
-
- val uri = UriUtil.parseUriFile(fileName)
- val scheme = uri?.scheme
- if (scheme != null && scheme.isNotEmpty() && scheme.equals("file", ignoreCase = true)) {
- val dbFile = File(uri.path!!)
- if (!dbFile.exists()) {
- throw FileNotFoundException()
- }
- }
- }
-
/*
* -------------------------
* Standard Launch
@@ -624,10 +654,9 @@ class PasswordActivity : StylishActivity(),
@Throws(FileNotFoundException::class)
fun launch(
activity: Activity,
- fileName: String,
- keyFile: String) {
- verifyFileNameUriFromLaunch(fileName)
- buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
+ databaseFile: Uri,
+ keyFile: Uri?) {
+ buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
activity.startActivity(intent)
}
}
@@ -641,12 +670,10 @@ class PasswordActivity : StylishActivity(),
@Throws(FileNotFoundException::class)
fun launchForKeyboardResult(
activity: Activity,
- fileName: String,
- keyFile: String) {
- verifyFileNameUriFromLaunch(fileName)
-
- buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
- KeyboardHelper.startActivityForKeyboardSelection(activity, intent)
+ databaseFile: Uri,
+ keyFile: Uri?) {
+ buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
+ EntrySelectionHelper.startActivityForEntrySelection(activity, intent)
}
}
@@ -660,20 +687,18 @@ class PasswordActivity : StylishActivity(),
@Throws(FileNotFoundException::class)
fun launchForAutofillResult(
activity: Activity,
- fileName: String,
- keyFile: String,
+ databaseFile: Uri,
+ keyFile: Uri?,
assistStructure: AssistStructure?) {
- verifyFileNameUriFromLaunch(fileName)
-
if (assistStructure != null) {
- buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
+ buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
AutofillHelper.startActivityForAutofillResult(
activity,
intent,
assistStructure)
}
} else {
- launch(activity, fileName, keyFile)
+ launch(activity, databaseFile, keyFile)
}
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt
index 5059adea4..a71a8f09b 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt
@@ -25,16 +25,16 @@ 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 com.google.android.material.textfield.TextInputLayout
+import androidx.fragment.app.DialogFragment
+import androidx.appcompat.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.activities.helpers.KeyFileHelper
+import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
import com.kunzisoft.keepass.utils.UriUtil
class AssignMasterKeyDialogFragment : DialogFragment() {
@@ -43,15 +43,39 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
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 passwordView: TextView? = null
+ private var passwordRepeatTextInputLayout: TextInputLayout? = null
+ private var passwordRepeatView: TextView? = null
+
+ private var keyFileTextInputLayout: TextInputLayout? = null
private var keyFileCheckBox: CompoundButton? = null
private var keyFileView: TextView? = null
private var mListener: AssignPasswordDialogListener? = null
- private var mKeyFileHelper: KeyFileHelper? = null
+ private var mOpenFileHelper: OpenFileHelper? = null
+
+ private val passwordTextWatcher = 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
+ }
+ }
+
+ private val keyFileTextWatcher = 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
+ }
+ }
interface AssignPasswordDialogListener {
fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?,
@@ -60,12 +84,12 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
keyFileChecked: Boolean, keyFile: Uri?)
}
- override fun onAttach(activity: Context?) {
+ override fun onAttach(activity: Context) {
super.onAttach(activity)
try {
- mListener = activity as AssignPasswordDialogListener?
+ mListener = activity as AssignPasswordDialogListener
} catch (e: ClassCastException) {
- throw ClassCastException(activity?.toString()
+ throw ClassCastException(activity.toString()
+ " must implement " + AssignPasswordDialogListener::class.java.name)
}
}
@@ -83,33 +107,17 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
.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)
+ passwordView = rootView?.findViewById(R.id.pass_password)
+ passwordRepeatTextInputLayout = rootView?.findViewById(R.id.password_repeat_input_layout)
+ passwordRepeatView = rootView?.findViewById(R.id.pass_conf_password)
+ keyFileTextInputLayout = rootView?.findViewById(R.id.keyfile_input_layout)
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
- }
- })
- mKeyFileHelper = KeyFileHelper(this)
+ mOpenFileHelper = OpenFileHelper(this)
rootView?.findViewById(R.id.browse_button)?.setOnClickListener { view ->
- mKeyFileHelper?.openFileOnClickViewListener?.onClick(view) }
+ mOpenFileHelper?.openFileOnClickViewListener?.onClick(view) }
val dialog = builder.create()
@@ -149,20 +157,35 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
return super.onCreateDialog(savedInstanceState)
}
+ override fun onResume() {
+ super.onResume()
+
+ // To check checkboxes if a text is present
+ passwordView?.addTextChangedListener(passwordTextWatcher)
+ keyFileView?.addTextChangedListener(keyFileTextWatcher)
+ }
+
+ override fun onPause() {
+ super.onPause()
+
+ passwordView?.removeTextChangedListener(passwordTextWatcher)
+ keyFileView?.removeTextChangedListener(keyFileTextWatcher)
+ }
+
private fun verifyPassword(): Boolean {
var error = false
if (passwordCheckBox != null
&& passwordCheckBox!!.isChecked
- && passView != null
- && passConfView != null) {
- mMasterPassword = passView!!.text.toString()
- val confPassword = passConfView!!.text.toString()
+ && passwordView != null
+ && passwordRepeatView != null) {
+ mMasterPassword = passwordView!!.text.toString()
+ val confPassword = passwordRepeatView!!.text.toString()
// Verify that passwords match
if (mMasterPassword != confPassword) {
error = true
// Passwords do not match
- Toast.makeText(context, R.string.error_pass_match, Toast.LENGTH_LONG).show()
+ passwordRepeatTextInputLayout?.error = getString(R.string.error_pass_match)
}
if (mMasterPassword == null || mMasterPassword!!.isEmpty()) {
@@ -177,13 +200,12 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
var error = false
if (keyFileCheckBox != null
&& keyFileCheckBox!!.isChecked) {
- val keyFile = UriUtil.parseUriFile(keyFileView?.text?.toString())
- mKeyFile = keyFile
- // Verify that a keyfile is set
- if (keyFile == null || keyFile.toString().isEmpty()) {
+ UriUtil.parse(keyFileView?.text?.toString())?.let { uri ->
+ mKeyFile = uri
+ } ?: run {
error = true
- Toast.makeText(context, R.string.error_nokeyfile, Toast.LENGTH_LONG).show()
+ keyFileTextInputLayout?.error = getString(R.string.error_nokeyfile)
}
}
return error
@@ -224,9 +246,9 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
- mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data
+ mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
) { uri ->
- UriUtil.parseUriFile(uri)?.let { pathUri ->
+ uri?.let { pathUri ->
keyFileCheckBox?.isChecked = true
keyFileView?.text = pathUri.toString()
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/BrowserDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/BrowserDialogFragment.kt
index 80c9f04cb..a981e04d0 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/BrowserDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/BrowserDialogFragment.kt
@@ -21,11 +21,12 @@ package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog
import android.os.Bundle
-import android.support.v4.app.DialogFragment
-import android.support.v7.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.appcompat.app.AlertDialog
import android.widget.Button
+import android.widget.TextView
import com.kunzisoft.keepass.R
-import com.kunzisoft.keepass.utils.Util
+import com.kunzisoft.keepass.utils.UriUtil
class BrowserDialogFragment : DialogFragment() {
@@ -37,15 +38,18 @@ class BrowserDialogFragment : DialogFragment() {
builder.setView(root)
.setNegativeButton(R.string.cancel) { _, _ -> }
- val market = root.findViewById