diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..0d7eaced0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,36 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**KeePass DX (please complete the following information):**
+ - Version: [e.g. 2.5.0.0beta23]
+ - Build: [e.g. Free]
+
+**Android (please complete the following information):**
+ - Device: [e.g. GalaxyS8]
+ - Version [e.g. 8.1]
+ - Browser [e.g. Chrome]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..4fe86d5ec
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: feature
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/CHANGELOG b/CHANGELOG
index 80e506c10..78978fcb2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
KeepassDX (2.5.0.0beta24)
- * Add settings
+ * Add settings (Color, Security, Master Key)
* Show history of each entry
+ * Auto repair database for nodes with same UUID
* Fix settings
* Fix edit group
* Fix small bugs
diff --git a/app/build.gradle b/app/build.gradle
index fe67efd82..29dded4b0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,6 +6,7 @@ apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
+ ndkVersion "20.0.5594570"
defaultConfig {
applicationId "com.kunzisoft.keepass"
@@ -99,6 +100,8 @@ dependencies {
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
// Time
implementation 'joda-time:joda-time:2.9.9'
+ // Color
+ implementation 'com.github.Kunzisoft:AndroidClearChroma:2.3'
// Education
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0'
// Apache Commons Collections
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b19a964e5..69114e490 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -131,7 +131,8 @@
+ android:label="@string/keyboard_name"
+ android:exported="true">
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 fd15681be..e187a21c4 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt
@@ -50,6 +50,7 @@ import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.view.EntryContentsView
+import java.util.*
class EntryActivity : LockingHideActivity() {
@@ -86,7 +87,7 @@ class EntryActivity : LockingHideActivity() {
// Get Entry from UUID
try {
- val keyEntry: PwNodeId<*> = intent.getParcelableExtra(KEY_ENTRY)
+ val keyEntry: PwNodeId = intent.getParcelableExtra(KEY_ENTRY)
mEntry = currentDatabase.getEntryById(keyEntry)
} catch (e: ClassCastException) {
Log.e(TAG, "Unable to retrieve the entry key")
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 c4051ac92..f9f94712a 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt
@@ -44,6 +44,7 @@ import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.view.EntryEditContentsView
+import java.util.*
class EntryEditActivity : LockingHideActivity(),
IconPickerDialogFragment.IconPickerListener,
@@ -90,7 +91,7 @@ class EntryEditActivity : LockingHideActivity(),
mDatabase = Database.getInstance()
// Entry is retrieve, it's an entry to update
- intent.getParcelableExtra>(KEY_ENTRY)?.let {
+ intent.getParcelableExtra>(KEY_ENTRY)?.let {
mIsNew = false
// Create an Entry copy to modify from the database entry
mEntry = mDatabase?.getEntryById(it)
@@ -176,7 +177,7 @@ class EntryEditActivity : LockingHideActivity(),
// Set info in view
entryEditContentsView?.apply {
title = newEntry.title
- username = newEntry.username
+ username = if (newEntry.username.isEmpty()) mDatabase?.defaultUsername ?:"" else newEntry.username
url = newEntry.url
password = newEntry.password
notes = newEntry.notes
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 5904e5a3b..69d45b087 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
@@ -311,7 +311,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
private val keyFileUri: Uri?) : ActionRunnable() {
override fun run() {
- finishRun(true, null)
+ finishRun(true)
}
override fun onFinishRun(result: Result) {
@@ -353,7 +353,8 @@ class FileDatabaseSelectActivity : StylishActivity(),
if (requestCode == CREATE_FILE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
mDatabaseFileUri = data?.data
if (mDatabaseFileUri != null) {
- AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog")
+ AssignMasterKeyDialogFragment.getInstance(true)
+ .show(supportFragmentManager, "passwordDialog")
}
// else {
// TODO Show error
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 6184bc966..d48ccccb6 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt
@@ -28,7 +28,6 @@ import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.os.Handler
-import android.util.AttributeSet
import android.util.Log
import android.view.Menu
import android.view.MenuItem
@@ -78,6 +77,7 @@ class GroupActivity : LockingActivity(),
private var searchTitleView: View? = null
private var toolbarPaste: Toolbar? = null
private var iconView: ImageView? = null
+ private var numberChildrenView: TextView? = null
private var modeTitleView: TextView? = null
private var addNodeButtonView: AddNodeButtonView? = null
private var groupNameView: TextView? = null
@@ -110,7 +110,8 @@ class GroupActivity : LockingActivity(),
setContentView(layoutInflater.inflate(R.layout.activity_group, null))
// Initialize views
- iconView = findViewById(R.id.icon)
+ iconView = findViewById(R.id.group_icon)
+ numberChildrenView = findViewById(R.id.group_numbers)
addNodeButtonView = findViewById(R.id.add_node_button)
toolbar = findViewById(R.id.toolbar)
searchTitleView = findViewById(R.id.search_title)
@@ -361,6 +362,16 @@ class GroupActivity : LockingActivity(),
}
}
+ // Assign number of children
+ numberChildrenView?.apply {
+ if (PreferencesUtil.showNumberEntries(context)) {
+ text = mCurrentGroup?.getChildEntries(true)?.size?.toString() ?: ""
+ visibility = View.VISIBLE
+ } else {
+ visibility = View.GONE
+ }
+ }
+
// Show selection mode message if needed
if (mSelectionMode) {
modeTitleView?.visibility = View.VISIBLE
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 3346deb1f..ac6e22cf3 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/ListNodesFragment.kt
@@ -207,7 +207,7 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
R.id.menu_sort -> {
context?.let { context ->
val sortDialogFragment: SortDialogFragment =
- if (Database.getInstance().isRecycleBinAvailable
+ if (Database.getInstance().allowRecycleBin
&& Database.getInstance().isRecycleBinEnabled) {
SortDialogFragment.getInstance(
PreferencesUtil.getListSort(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 bfb85ab32..4173f40e6 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt
@@ -44,6 +44,7 @@ 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.DuplicateUuidDialog
import com.kunzisoft.keepass.activities.dialogs.FingerPrintExplanationDialog
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
@@ -61,6 +62,8 @@ import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
+import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
+import com.kunzisoft.keepass.database.search.SearchDbHelper
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.MenuUtil
@@ -69,7 +72,6 @@ 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
class PasswordActivity : StylishActivity() {
@@ -87,6 +89,8 @@ class PasswordActivity : StylishActivity() {
private var enableButtonOnCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
private var mDatabaseFileUri: Uri? = null
+ private var mDatabaseKeyFileUri: Uri? = null
+
private var prefs: SharedPreferences? = null
private var mRememberKeyFile: Boolean = false
@@ -101,8 +105,7 @@ class PasswordActivity : StylishActivity() {
prefs = PreferenceManager.getDefaultSharedPreferences(this)
- mRememberKeyFile = prefs!!.getBoolean(getString(R.string.keyfile_key),
- resources.getBoolean(R.bool.keyfile_default))
+ mRememberKeyFile = PreferencesUtil.rememberKeyFiles(this)
setContentView(R.layout.activity_password)
@@ -215,6 +218,7 @@ class PasswordActivity : StylishActivity() {
private fun onPostInitUri(databaseFileUri: Uri?, keyFileUri: Uri?) {
mDatabaseFileUri = databaseFileUri
+ mDatabaseKeyFileUri = keyFileUri
// Define title
databaseFileUri?.let {
@@ -236,11 +240,13 @@ class PasswordActivity : StylishActivity() {
newDefaultFileName = databaseFileUri ?: newDefaultFileName
}
- newDefaultFileName?.let {
- prefs?.edit()?.apply {
+ prefs?.edit()?.apply {
+ newDefaultFileName?.let {
putString(KEY_DEFAULT_DATABASE_PATH, newDefaultFileName.toString())
- apply()
+ } ?: kotlin.run {
+ remove(KEY_DEFAULT_DATABASE_PATH)
}
+ apply()
}
val backupManager = BackupManager(this@PasswordActivity)
@@ -384,14 +390,18 @@ class PasswordActivity : StylishActivity() {
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, cipherDatabaseEntity)
+ verifyKeyFileCheckbox(keyFile)
+ loadDatabase(mDatabaseFileUri, keyPassword, mDatabaseKeyFileUri, cipherDatabaseEntity)
}
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)
+ verifyKeyFileCheckbox(keyFile)
+ loadDatabase(mDatabaseFileUri, password, mDatabaseKeyFileUri)
+ }
+
+ private fun verifyKeyFileCheckbox(keyFile: Uri?) {
+ mDatabaseKeyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
}
private fun removePassword() {
@@ -399,7 +409,10 @@ class PasswordActivity : StylishActivity() {
checkboxPasswordView?.isChecked = false
}
- private fun loadDatabase(password: String?, keyFile: Uri?, cipherDatabaseEntity: CipherDatabaseEntity? = null) {
+ private fun loadDatabase(databaseFileUri: Uri?,
+ password: String?,
+ keyFileUri: Uri?,
+ cipherDatabaseEntity: CipherDatabaseEntity? = null) {
runOnUiThread {
if (PreferencesUtil.deletePasswordAfterConnexionAttempt(this)) {
@@ -411,65 +424,119 @@ class PasswordActivity : StylishActivity() {
val database = Database.getInstance()
database.closeAndClear(applicationContext.filesDir)
- mDatabaseFileUri?.let { databaseUri ->
- // Show the progress dialog and load the database
- ProgressDialogThread(this,
- { progressTaskUpdater ->
- LoadDatabaseRunnable(
- WeakReference(this@PasswordActivity),
- database,
- databaseUri,
- password,
- keyFile,
- progressTaskUpdater,
- AfterLoadingDatabase(database, password, cipherDatabaseEntity))
- },
- R.string.loading_database).start()
- }
- }
+ databaseFileUri?.let { databaseUri ->
- /**
- * Called after verify and try to opening the database
- */
- private inner class AfterLoadingDatabase(val database: Database, val password: String?,
- val cipherDatabaseEntity: CipherDatabaseEntity? = null)
- : ActionRunnable() {
+ val onFinishLoadDatabase = object: ActionRunnable() {
+ override fun onFinishRun(result: Result) {
+ runOnUiThread {
+ // Recheck fingerprint if error
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (PreferencesUtil.isBiometricUnlockEnable(this@PasswordActivity)) {
+ // Stay with the same mode and init it
+ advancedUnlockedManager?.initBiometricMode()
+ }
+ }
- override fun onFinishRun(result: Result) {
- runOnUiThread {
- // Recheck fingerprint if error
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (PreferencesUtil.isBiometricUnlockEnable(this@PasswordActivity)) {
- // Stay with the same mode and init it
- advancedUnlockedManager?.initBiometricMode()
- }
- }
-
- if (result.isSuccess) {
- // Remove the password in view in all cases
- removePassword()
-
- // Register the biometric
- if (cipherDatabaseEntity != null) {
- CipherDatabaseAction.getInstance(this@PasswordActivity)
- .addOrUpdateCipherDatabase(cipherDatabaseEntity) {
- checkAndLaunchGroupActivity(database, password)
+ if (result.isSuccess) {
+ // Save keyFile in app database
+ if (mRememberKeyFile) {
+ mDatabaseFileUri?.let { databaseUri ->
+ saveKeyFileData(databaseUri, mDatabaseKeyFileUri)
}
- } else {
- checkAndLaunchGroupActivity(database, password)
- }
+ }
- } else {
- if (result.message != null && result.message!!.isNotEmpty()) {
- Snackbar.make(activity_password_coordinator_layout, result.message!!, Snackbar.LENGTH_LONG).asError().show()
+ // Remove the password in view in all cases
+ removePassword()
+
+ // Register the biometric
+ if (cipherDatabaseEntity != null) {
+ CipherDatabaseAction.getInstance(this@PasswordActivity)
+ .addOrUpdateCipherDatabase(cipherDatabaseEntity) {
+ checkAndLaunchGroupActivity(database, password, keyFileUri)
+ }
+ } else {
+ checkAndLaunchGroupActivity(database, password, keyFileUri)
+ }
+
+ } else {
+ var resultError = ""
+ val resultException = result.exception
+ val resultMessage = result.message
+
+ if (resultException != null) {
+ resultError = resultException.getLocalizedMessage(resources)
+ if (resultException is LoadDatabaseDuplicateUuidException)
+ showLoadDatabaseDuplicateUuidMessage {
+ showProgressDialogAndLoadDatabase(database,
+ databaseUri,
+ password,
+ keyFileUri,
+ true,
+ this)
+ }
+ }
+
+ if (resultMessage != null && resultMessage.isNotEmpty()) {
+ resultError = "$resultError $resultMessage"
+ }
+
+ Log.e(TAG, resultError, resultException)
+
+ Snackbar.make(activity_password_coordinator_layout, resultError, Snackbar.LENGTH_LONG).asError().show()
+ }
}
}
}
+
+ // Show the progress dialog and load the database
+ showProgressDialogAndLoadDatabase(database,
+ databaseUri,
+ password,
+ keyFileUri,
+ false,
+ onFinishLoadDatabase)
}
}
- private fun checkAndLaunchGroupActivity(database: Database, password: String?) {
- if (database.validatePasswordEncoding(password)) {
+ private fun showProgressDialogAndLoadDatabase(database: Database,
+ databaseUri: Uri,
+ password: String?,
+ keyFile: Uri?,
+ fixDuplicateUUID: Boolean,
+ onFinishLoadDatabase: ActionRunnable) {
+ ProgressDialogThread(this,
+ { progressTaskUpdater ->
+ LoadDatabaseRunnable(
+ database,
+ databaseUri,
+ password,
+ keyFile,
+ this@PasswordActivity.contentResolver,
+ this@PasswordActivity.filesDir,
+ SearchDbHelper(PreferencesUtil.omitBackup(this@PasswordActivity)),
+ fixDuplicateUUID,
+ progressTaskUpdater,
+ onFinishLoadDatabase)
+ },
+ R.string.loading_database).start()
+ }
+
+ private fun showLoadDatabaseDuplicateUuidMessage(loadDatabaseWithFix: (() -> Unit)? = null) {
+ DuplicateUuidDialog().apply {
+ positiveAction = loadDatabaseWithFix
+ }.show(supportFragmentManager, "duplicateUUIDDialog")
+ }
+
+ private fun saveKeyFileData(databaseUri: Uri, keyUri: Uri?) {
+ var keyFileUri = keyUri
+ if (!mRememberKeyFile) {
+ keyFileUri = null
+ }
+ FileDatabaseHistoryAction.getInstance(this).addOrUpdateDatabaseUri(databaseUri, keyFileUri)
+ }
+
+ private fun checkAndLaunchGroupActivity(database: Database, password: String?, keyFileUri: Uri?) {
+ if (database.validatePasswordEncoding(password, keyFileUri != null)) {
launchGroupActivity()
} else {
PasswordEncodingDialogFragment().apply {
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 8672ab1e0..f78a239ea 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
@@ -45,6 +45,8 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
private var rootView: View? = null
private var passwordCheckBox: CompoundButton? = null
+
+ private var passwordTextInputLayout: TextInputLayout? = null
private var passwordView: TextView? = null
private var passwordRepeatTextInputLayout: TextInputLayout? = null
private var passwordRepeatView: TextView? = null
@@ -96,6 +98,13 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
+
+ var allowNoMasterKey = false
+ arguments?.apply {
+ if (containsKey(ALLOW_NO_MASTER_KEY_ARG))
+ allowNoMasterKey = getBoolean(ALLOW_NO_MASTER_KEY_ARG, false)
+ }
+
val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater
@@ -104,9 +113,10 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
.setTitle(R.string.assign_master_key)
// Add action buttons
.setPositiveButton(android.R.string.ok) { _, _ -> }
- .setNegativeButton(R.string.cancel) { _, _ -> }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
passwordCheckBox = rootView?.findViewById(R.id.password_checkbox)
+ passwordTextInputLayout = rootView?.findViewById(R.id.password_input_layout)
passwordView = rootView?.findViewById(R.id.pass_password)
passwordRepeatTextInputLayout = rootView?.findViewById(R.id.password_repeat_input_layout)
passwordRepeatView = rootView?.findViewById(R.id.pass_conf_password)
@@ -132,7 +142,11 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
var error = verifyPassword() || verifyFile()
if (!passwordCheckBox!!.isChecked && !keyFileCheckBox!!.isChecked) {
error = true
- showNoKeyConfirmationDialog()
+ if (allowNoMasterKey)
+ showNoKeyConfirmationDialog()
+ else {
+ passwordTextInputLayout?.error = getString(R.string.error_disallow_no_credentials)
+ }
}
if (!error) {
mListener?.onAssignKeyDialogPositiveClick(
@@ -193,6 +207,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
showEmptyPasswordConfirmationDialog()
}
}
+
return error
}
@@ -223,7 +238,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
this@AssignMasterKeyDialogFragment.dismiss()
}
}
- .setNegativeButton(R.string.cancel) { _, _ -> }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show()
}
}
@@ -238,7 +253,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
keyFileCheckBox!!.isChecked, mKeyFile)
this@AssignMasterKeyDialogFragment.dismiss()
}
- .setNegativeButton(R.string.cancel) { _, _ -> }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show()
}
}
@@ -255,4 +270,17 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
}
}
}
+
+ companion object {
+
+ private const val ALLOW_NO_MASTER_KEY_ARG = "ALLOW_NO_MASTER_KEY_ARG"
+
+ fun getInstance(allowNoMasterKey: Boolean): AssignMasterKeyDialogFragment {
+ val fragment = AssignMasterKeyDialogFragment()
+ val args = Bundle()
+ args.putBoolean(ALLOW_NO_MASTER_KEY_ARG, allowNoMasterKey)
+ fragment.arguments = args
+ return fragment
+ }
+ }
}
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 a981e04d0..3dd5514ac 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
@@ -36,7 +36,7 @@ class BrowserDialogFragment : DialogFragment() {
// Get the layout inflater
val root = activity.layoutInflater.inflate(R.layout.fragment_browser_install, null)
builder.setView(root)
- .setNegativeButton(R.string.cancel) { _, _ -> }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
val textDescription = root.findViewById(R.id.file_manager_install_description)
textDescription.text = getString(R.string.file_manager_install_description)
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DuplicateUuidDialog.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DuplicateUuidDialog.kt
new file mode 100644
index 000000000..219dd9c01
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/DuplicateUuidDialog.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeePass DX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.activities.dialogs
+
+import android.app.Dialog
+import android.os.Bundle
+import androidx.fragment.app.DialogFragment
+import com.kunzisoft.keepass.R
+
+class DuplicateUuidDialog : DialogFragment() {
+
+ var positiveAction: (() -> Unit)? = null
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ activity?.let { activity ->
+ // Use the Builder class for convenient dialog construction
+ val builder = androidx.appcompat.app.AlertDialog.Builder(activity).apply {
+ val message = getString(R.string.contains_duplicate_uuid) +
+ "\n\n" + getString(R.string.contains_duplicate_uuid_procedure)
+ setMessage(message)
+ setPositiveButton(getString(android.R.string.ok)) { _, _ ->
+ positiveAction?.invoke()
+ dismiss()
+ }
+ setNegativeButton(getString(android.R.string.cancel)) { _, _ -> dismiss() }
+ }
+ // Create the AlertDialog object and return it
+ return builder.create()
+ }
+ return super.onCreateDialog(savedInstanceState)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ this.dismiss()
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt
index 350049fba..156a0be88 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GeneratePasswordDialogFragment.kt
@@ -114,7 +114,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
dismiss()
}
- .setNegativeButton(R.string.cancel) { _, _ ->
+ .setNegativeButton(android.R.string.cancel) { _, _ ->
val bundle = Bundle()
mListener?.cancelPassword(bundle)
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt
index 032f80cb8..8f126d083 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/GroupEditDialogFragment.kt
@@ -122,7 +122,7 @@ class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconP
val builder = AlertDialog.Builder(activity)
builder.setView(root)
.setPositiveButton(android.R.string.ok, null)
- .setNegativeButton(R.string.cancel) { _, _ ->
+ .setNegativeButton(android.R.string.cancel) { _, _ ->
editGroupListener?.cancelEditGroup(
editGroupDialogAction,
nameTextView?.text?.toString(),
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconPickerDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconPickerDialogFragment.kt
index 1e218ccff..7b0691ba9 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconPickerDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/IconPickerDialogFragment.kt
@@ -77,7 +77,7 @@ class IconPickerDialogFragment : DialogFragment() {
dismiss()
}
- builder.setNegativeButton(R.string.cancel) { _, _ -> this@IconPickerDialogFragment.dialog?.cancel() }
+ builder.setNegativeButton(android.R.string.cancel) { _, _ -> this@IconPickerDialogFragment.dialog?.cancel() }
return builder.create()
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt
index 87b1baa87..4cb8f1234 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/PasswordEncodingDialogFragment.kt
@@ -35,7 +35,7 @@ class PasswordEncodingDialogFragment : DialogFragment() {
val builder = AlertDialog.Builder(activity)
builder.setMessage(activity.getString(R.string.warning_password_encoding)).setTitle(R.string.warning)
builder.setPositiveButton(android.R.string.ok, positiveButtonClickListener)
- builder.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.cancel() }
+ builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() }
return builder.create()
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt
index 7f67091ab..9c17defbb 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt
@@ -83,7 +83,7 @@ class SortDialogFragment : DialogFragment() {
// Add action buttons
.setPositiveButton(android.R.string.ok
) { _, _ -> mListener?.onSortSelected(mSortNodeEnum, mAscending, mGroupsBefore, mRecycleBinBottom) }
- .setNegativeButton(R.string.cancel) { _, _ -> }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
val ascendingView = rootView.findViewById(R.id.sort_selection_ascending)
// Check if is ascending or descending
diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockedManager.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockedManager.kt
index 2698d32cd..158e260df 100644
--- a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockedManager.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockedManager.kt
@@ -249,11 +249,18 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
biometricUnlockDatabaseHelper = null
}
+ // Only to fix multiple fingerprint menu #332
+ private var addBiometricMenuInProgress = false
fun inflateOptionsMenu(menuInflater: MenuInflater, menu: Menu) {
- cipherDatabaseAction.containsCipherDatabase(databaseFileUri) {
- if ((biometricMode != Mode.UNAVAILABLE
- && biometricMode != Mode.NOT_CONFIGURED) && it)
- menuInflater.inflate(R.menu.advanced_unlock, menu)
+ if (!addBiometricMenuInProgress) {
+ addBiometricMenuInProgress = true
+ cipherDatabaseAction.containsCipherDatabase(databaseFileUri) {
+ if ((biometricMode != Mode.UNAVAILABLE && biometricMode != Mode.NOT_CONFIGURED)
+ && it) {
+ menuInflater.inflate(R.menu.advanced_unlock, menu)
+ addBiometricMenuInProgress = false
+ }
+ }
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt
index a72f1317c..17de99ad7 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignPasswordInDatabaseRunnable.kt
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
import android.net.Uri
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.UriUtil
import java.io.IOException
@@ -63,7 +63,7 @@ open class AssignPasswordInDatabaseRunnable @JvmOverloads constructor(
// To save the database
super.run()
finishRun(true)
- } catch (e: InvalidKeyFileException) {
+ } catch (e: LoadDatabaseInvalidKeyFileException) {
erase(mBackupKey)
finishRun(false, e.message)
} catch (e: IOException) {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt
index 1b31b3274..b164fd70c 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt
@@ -19,118 +19,45 @@
*/
package com.kunzisoft.keepass.database.action
-import android.content.Context
+import android.content.ContentResolver
import android.net.Uri
-import android.preference.PreferenceManager
-import androidx.annotation.StringRes
-import android.util.Log
-import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.database.exception.*
-import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
+import com.kunzisoft.keepass.database.exception.LoadDatabaseException
+import com.kunzisoft.keepass.database.search.SearchDbHelper
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
-import java.io.FileNotFoundException
-import java.io.IOException
-import java.lang.ref.WeakReference
+import java.io.File
-class LoadDatabaseRunnable(private val mWeakContext: WeakReference,
- private val mDatabase: Database,
+class LoadDatabaseRunnable(private val mDatabase: Database,
private val mUri: Uri,
private val mPass: String?,
private val mKey: Uri?,
+ private val contentResolver: ContentResolver,
+ private val cacheDirectory: File,
+ private val mSearchHelper: SearchDbHelper,
+ private val mFixDuplicateUUID: Boolean,
private val progressTaskUpdater: ProgressTaskUpdater?,
nestedAction: ActionRunnable)
: ActionRunnable(nestedAction, executeNestedActionIfResultFalse = true) {
- private val mRememberKeyFile: Boolean
- get() {
- return mWeakContext.get()?.let {
- PreferenceManager.getDefaultSharedPreferences(it)
- .getBoolean(it.getString(R.string.keyfile_key),
- it.resources.getBoolean(R.bool.keyfile_default))
- } ?: true
- }
-
override fun run() {
try {
- mWeakContext.get()?.let {
- mDatabase.loadData(it, mUri, mPass, mKey, progressTaskUpdater)
- saveFileData(mUri, mKey)
- finishRun(true)
- } ?: finishRun(false, "Context null")
- } catch (e: ArcFourException) {
- catchError(e, R.string.error_arc4)
- return
- } catch (e: InvalidPasswordException) {
- catchError(e, R.string.invalid_password)
- return
- } catch (e: ContentFileNotFoundException) {
- catchError(e, R.string.file_not_found_content)
- return
- } catch (e: FileNotFoundException) {
- catchError(e, R.string.file_not_found)
- return
- } catch (e: IOException) {
- var messageId = R.string.error_load_database
- e.message?.let {
- if (it.contains("Hash failed with code"))
- messageId = R.string.error_load_database_KDF_memory
- }
- catchError(e, messageId, true)
- return
- } catch (e: KeyFileEmptyException) {
- catchError(e, R.string.keyfile_is_empty)
- return
- } catch (e: InvalidAlgorithmException) {
- catchError(e, R.string.invalid_algorithm)
- return
- } catch (e: InvalidKeyFileException) {
- catchError(e, R.string.keyfile_does_not_exist)
- return
- } catch (e: InvalidDBSignatureException) {
- catchError(e, R.string.invalid_db_sig)
- return
- } catch (e: InvalidDBVersionException) {
- catchError(e, R.string.unsupported_db_version)
- return
- } catch (e: InvalidDBException) {
- catchError(e, R.string.error_invalid_db)
- return
- } catch (e: OutOfMemoryError) {
- catchError(e, R.string.error_out_of_memory)
- return
- } catch (e: Exception) {
- catchError(e, R.string.error_load_database, true)
- return
+ mDatabase.loadData(mUri, mPass, mKey,
+ contentResolver,
+ cacheDirectory,
+ mSearchHelper,
+ mFixDuplicateUUID,
+ progressTaskUpdater)
+ finishRun(true)
}
- }
-
- private fun catchError(e: Throwable, @StringRes messageId: Int, addThrowableMessage: Boolean = false) {
- var errorMessage = mWeakContext.get()?.getString(messageId)
- Log.e(TAG, errorMessage, e)
- if (addThrowableMessage)
- errorMessage = errorMessage + " " + e.localizedMessage
- finishRun(false, errorMessage)
- }
-
- private fun saveFileData(uri: Uri, key: Uri?) {
- var keyFileUri = key
- if (!mRememberKeyFile) {
- keyFileUri = null
- }
- mWeakContext.get()?.let {
- FileDatabaseHistoryAction.getInstance(it).addOrUpdateDatabaseUri(uri, keyFileUri)
+ catch (e: LoadDatabaseException) {
+ finishRun(false, e)
}
}
override fun onFinishRun(result: Result) {
if (!result.isSuccess) {
- mDatabase.closeAndClear(mWeakContext.get()?.filesDir)
+ mDatabase.closeAndClear(cacheDirectory)
}
}
-
- companion object {
- private val TAG = LoadDatabaseRunnable::class.java.name
- }
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt
index bba0a529f..62aa0956e 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/SaveDatabaseRunnable.kt
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.database.exception.PwDbOutputException
+import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.tasks.ActionRunnable
import java.io.IOException
@@ -37,7 +37,7 @@ abstract class SaveDatabaseRunnable(protected var context: Context,
database.saveData(context.contentResolver)
} catch (e: IOException) {
finishRun(false, e.message)
- } catch (e: PwDbOutputException) {
+ } catch (e: DatabaseOutputException) {
finishRun(false, e.message)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt
index c628ea951..c308e99e6 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt
@@ -20,7 +20,6 @@
package com.kunzisoft.keepass.database.element
import android.content.ContentResolver
-import android.content.Context
import android.content.res.Resources
import android.database.Cursor
import android.net.Uri
@@ -39,7 +38,6 @@ import com.kunzisoft.keepass.database.file.save.PwDbV3Output
import com.kunzisoft.keepass.database.file.save.PwDbV4Output
import com.kunzisoft.keepass.database.search.SearchDbHelper
import com.kunzisoft.keepass.icons.IconDrawableFactory
-import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.stream.LEDataInputStream
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.SingletonHolder
@@ -56,7 +54,8 @@ class Database {
private var pwDatabaseV4: PwDatabaseV4? = null
private var mUri: Uri? = null
- private var searchHelper: SearchDbHelper? = null
+ private var mSearchHelper: SearchDbHelper? = null
+
var isReadOnly = false
val drawFactory = IconDrawableFactory()
@@ -68,42 +67,120 @@ class Database {
return pwDatabaseV3?.iconFactory ?: pwDatabaseV4?.iconFactory ?: PwIconFactory()
}
- val name: String
+ val allowName: Boolean
+ get() = pwDatabaseV4 != null
+
+ var name: String
get() {
return pwDatabaseV4?.name ?: ""
}
+ set(name) {
+ pwDatabaseV4?.name = name
+ pwDatabaseV4?.nameChanged = PwDate()
+ }
- val description: String
+ val allowDescription: Boolean
+ get() = pwDatabaseV4 != null
+
+ var description: String
get() {
return pwDatabaseV4?.description ?: ""
}
+ set(description) {
+ pwDatabaseV4?.description = description
+ pwDatabaseV4?.descriptionChanged = PwDate()
+ }
+
+ val allowDefaultUsername: Boolean
+ get() = pwDatabaseV4 != null
+ // TODO get() = pwDatabaseV3 != null || pwDatabaseV4 != null
var defaultUsername: String
get() {
- return pwDatabaseV4?.defaultUserName ?: ""
+ return pwDatabaseV4?.defaultUserName ?: "" // TODO pwDatabaseV3 default username
}
set(username) {
pwDatabaseV4?.defaultUserName = username
pwDatabaseV4?.defaultUserNameChanged = PwDate()
}
+ val allowCustomColor: Boolean
+ get() = pwDatabaseV4 != null
+ // TODO get() = pwDatabaseV3 != null || pwDatabaseV4 != null
+
+ // with format "#000000"
+ var customColor: String
+ get() {
+ return pwDatabaseV4?.color ?: "" // TODO pwDatabaseV3 color
+ }
+ set(value) {
+ // TODO Check color string
+ pwDatabaseV4?.color = value
+ }
+
+ val version: String
+ get() = pwDatabaseV3?.version ?: pwDatabaseV4?.version ?: "-"
+
+ val allowDataCompression: Boolean
+ get() = pwDatabaseV4 != null
+
val availableCompressionAlgorithms: List
get() = pwDatabaseV4?.availableCompressionAlgorithms ?: ArrayList()
- val compressionAlgorithm: PwCompressionAlgorithm?
+ var compressionAlgorithm: PwCompressionAlgorithm?
get() = pwDatabaseV4?.compressionAlgorithm
+ set(value) {
+ value?.let {
+ pwDatabaseV4?.compressionAlgorithm = it
+ }
+ }
+
+ val allowNoMasterKey: Boolean
+ get() = pwDatabaseV4 != null
+
+ val allowEncryptionAlgorithmModification: Boolean
+ get() = availableEncryptionAlgorithms.size > 1
+
+ fun getEncryptionAlgorithmName(resources: Resources): String {
+ return pwDatabaseV3?.encryptionAlgorithm?.getName(resources)
+ ?: pwDatabaseV4?.encryptionAlgorithm?.getName(resources)
+ ?: ""
+ }
val availableEncryptionAlgorithms: List
get() = pwDatabaseV3?.availableEncryptionAlgorithms ?: pwDatabaseV4?.availableEncryptionAlgorithms ?: ArrayList()
- val encryptionAlgorithm: PwEncryptionAlgorithm?
+ var encryptionAlgorithm: PwEncryptionAlgorithm?
get() = pwDatabaseV3?.encryptionAlgorithm ?: pwDatabaseV4?.encryptionAlgorithm
+ set(algorithm) {
+ algorithm?.let {
+ pwDatabaseV4?.encryptionAlgorithm = algorithm
+ pwDatabaseV4?.setDataEngine(algorithm.cipherEngine)
+ pwDatabaseV4?.dataCipher = algorithm.dataCipher
+ }
+ }
val availableKdfEngines: List
get() = pwDatabaseV3?.kdfAvailableList ?: pwDatabaseV4?.kdfAvailableList ?: ArrayList()
- val kdfEngine: KdfEngine?
+ val allowKdfModification: Boolean
+ get() = availableKdfEngines.size > 1
+
+ var kdfEngine: KdfEngine?
get() = pwDatabaseV3?.kdfEngine ?: pwDatabaseV4?.kdfEngine
+ set(kdfEngine) {
+ kdfEngine?.let {
+ if (pwDatabaseV4?.kdfParameters?.uuid != kdfEngine.defaultParameters.uuid)
+ pwDatabaseV4?.kdfParameters = kdfEngine.defaultParameters
+ numberKeyEncryptionRounds = kdfEngine.defaultKeyRounds
+ memoryUsage = kdfEngine.defaultMemoryUsage
+ parallelism = kdfEngine.defaultParallelism
+ }
+ }
+
+ fun getKeyDerivationName(resources: Resources): String {
+ return kdfEngine?.getName(resources) ?: ""
+ }
var numberKeyEncryptionRounds: Long
get() = pwDatabaseV3?.numberKeyEncryptionRounds ?: pwDatabaseV4?.numberKeyEncryptionRounds ?: 0
@@ -168,7 +245,7 @@ class Database {
* Determine if RecycleBin is available or not for this version of database
* @return true if RecycleBin available
*/
- val isRecycleBinAvailable: Boolean
+ val allowRecycleBin: Boolean
get() = pwDatabaseV4 != null
val isRecycleBinEnabled: Boolean
@@ -209,78 +286,95 @@ class Database {
this.mUri = databaseUri
}
- @Throws(IOException::class, InvalidDBException::class)
- fun loadData(ctx: Context, uri: Uri, password: String?, keyfile: Uri?, progressTaskUpdater: ProgressTaskUpdater?) {
+ @Throws(LoadDatabaseException::class)
+ fun loadData(uri: Uri, password: String?, keyfile: Uri?,
+ contentResolver: ContentResolver,
+ cacheDirectory: File,
+ searchHelper: SearchDbHelper,
+ fixDuplicateUUID: Boolean,
+ progressTaskUpdater: ProgressTaskUpdater?) {
- mUri = uri
- isReadOnly = false
- if (uri.scheme == "file") {
- val file = File(uri.path!!)
- isReadOnly = !file.canWrite()
- }
-
- // Pass Uris as InputStreams
- val inputStream: InputStream?
try {
- inputStream = UriUtil.getUriInputStream(ctx.contentResolver, uri)
- } catch (e: Exception) {
- Log.e("KPD", "Database::loadData", e)
- throw ContentFileNotFoundException.getInstance(uri)
- }
- // Pass KeyFile Uri as InputStreams
- var keyFileInputStream: InputStream? = null
- keyfile?.let {
+ mUri = uri
+ isReadOnly = false
+ if (uri.scheme == "file") {
+ val file = File(uri.path!!)
+ isReadOnly = !file.canWrite()
+ }
+
+ // Pass Uris as InputStreams
+ val inputStream: InputStream?
try {
- keyFileInputStream = UriUtil.getUriInputStream(ctx.contentResolver, keyfile)
+ inputStream = UriUtil.getUriInputStream(contentResolver, uri)
} catch (e: Exception) {
Log.e("KPD", "Database::loadData", e)
- throw ContentFileNotFoundException.getInstance(keyfile)
+ throw LoadDatabaseFileNotFoundException()
}
- }
- // Load Data
+ // Pass KeyFile Uri as InputStreams
+ var keyFileInputStream: InputStream? = null
+ keyfile?.let {
+ try {
+ keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyfile)
+ } catch (e: Exception) {
+ Log.e("KPD", "Database::loadData", e)
+ throw LoadDatabaseFileNotFoundException()
+ }
+ }
- val bufferedInputStream = BufferedInputStream(inputStream)
- if (!bufferedInputStream.markSupported()) {
- throw IOException("Input stream does not support mark.")
- }
+ // Load Data
- // We'll end up reading 8 bytes to identify the header. Might as well use two extra.
- bufferedInputStream.mark(10)
+ val bufferedInputStream = BufferedInputStream(inputStream)
+ if (!bufferedInputStream.markSupported()) {
+ throw IOException("Input stream does not support mark.")
+ }
- // Get the file directory to save the attachments
- val sig1 = LEDataInputStream.readInt(bufferedInputStream)
- val sig2 = LEDataInputStream.readInt(bufferedInputStream)
+ // We'll end up reading 8 bytes to identify the header. Might as well use two extra.
+ bufferedInputStream.mark(10)
- // Return to the start
- bufferedInputStream.reset()
+ // Get the file directory to save the attachments
+ val sig1 = LEDataInputStream.readInt(bufferedInputStream)
+ val sig2 = LEDataInputStream.readInt(bufferedInputStream)
- when {
- // Header of database V3
- PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3()
- .openDatabase(bufferedInputStream,
- password,
- keyFileInputStream,
- progressTaskUpdater))
+ // Return to the start
+ bufferedInputStream.reset()
- // Header of database V4
- PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(ctx.filesDir)
- .openDatabase(bufferedInputStream,
- password,
- keyFileInputStream,
- progressTaskUpdater))
+ when {
+ // Header of database V3
+ PwDbHeaderV3.matchesHeader(sig1, sig2) -> setDatabaseV3(ImporterV3()
+ .openDatabase(bufferedInputStream,
+ password,
+ keyFileInputStream,
+ progressTaskUpdater))
- // Header not recognized
- else -> throw InvalidDBSignatureException()
- }
+ // Header of database V4
+ PwDbHeaderV4.matchesHeader(sig1, sig2) -> setDatabaseV4(ImporterV4(
+ cacheDirectory,
+ fixDuplicateUUID)
+ .openDatabase(bufferedInputStream,
+ password,
+ keyFileInputStream,
+ progressTaskUpdater))
- try {
- searchHelper = SearchDbHelper(PreferencesUtil.omitBackup(ctx))
+ // Header not recognized
+ else -> throw LoadDatabaseSignatureException()
+ }
+
+ this.mSearchHelper = searchHelper
loaded = true
+
+ } catch (e: LoadDatabaseException) {
+ throw e
+ } catch (e: IOException) {
+ if (e.message?.contains("Hash failed with code") == true)
+ throw LoadDatabaseKDFMemoryException(e)
+ else
+ throw LoadDatabaseIOException(e)
+ } catch (e: OutOfMemoryError) {
+ throw LoadDatabaseNoMemoryException(e)
} catch (e: Exception) {
- Log.e(TAG, "Load can't be performed with this Database version", e)
- loaded = false
+ throw LoadDatabaseException(e)
}
}
@@ -292,7 +386,7 @@ class Database {
@JvmOverloads
fun search(str: String, max: Int = Integer.MAX_VALUE): GroupVersioned? {
- return searchHelper?.search(this, str, max)
+ return mSearchHelper?.search(this, str, max)
}
fun searchEntries(query: String): Cursor? {
@@ -343,14 +437,14 @@ class Database {
return entry
}
- @Throws(IOException::class, PwDbOutputException::class)
+ @Throws(IOException::class, DatabaseOutputException::class)
fun saveData(contentResolver: ContentResolver) {
mUri?.let {
saveData(contentResolver, it)
}
}
- @Throws(IOException::class, PwDbOutputException::class)
+ @Throws(IOException::class, DatabaseOutputException::class)
private fun saveData(contentResolver: ContentResolver, uri: Uri) {
val errorMessage = "Failed to store database."
@@ -419,72 +513,13 @@ class Database {
loaded = false
}
- fun getVersion(): String {
- return pwDatabaseV3?.version ?: pwDatabaseV4?.version ?: "unknown"
- }
-
- fun containsName(): Boolean {
- pwDatabaseV4?.let { return true }
- return false
- }
-
- fun assignName(name: String) {
- pwDatabaseV4?.name = name
- pwDatabaseV4?.nameChanged = PwDate()
- }
-
- fun containsDescription(): Boolean {
- pwDatabaseV4?.let { return true }
- return false
- }
-
- fun assignDescription(description: String) {
- pwDatabaseV4?.description = description
- pwDatabaseV4?.descriptionChanged = PwDate()
- }
-
- fun assignCompressionAlgorithm(algorithm: PwCompressionAlgorithm) {
- pwDatabaseV4?.compressionAlgorithm = algorithm
- // TODO Compression
- }
-
- fun allowEncryptionAlgorithmModification(): Boolean {
- return availableEncryptionAlgorithms.size > 1
- }
-
- fun assignEncryptionAlgorithm(algorithm: PwEncryptionAlgorithm) {
- pwDatabaseV4?.encryptionAlgorithm = algorithm
- pwDatabaseV4?.setDataEngine(algorithm.cipherEngine)
- pwDatabaseV4?.dataCipher = algorithm.dataCipher
- }
-
- fun getEncryptionAlgorithmName(resources: Resources): String {
- return pwDatabaseV3?.encryptionAlgorithm?.getName(resources) ?: pwDatabaseV4?.encryptionAlgorithm?.getName(resources) ?: ""
- }
-
- fun allowKdfModification(): Boolean {
- return availableKdfEngines.size > 1
- }
-
- fun assignKdfEngine(kdfEngine: KdfEngine) {
- if (pwDatabaseV4?.kdfParameters?.uuid != kdfEngine.defaultParameters.uuid)
- pwDatabaseV4?.kdfParameters = kdfEngine.defaultParameters
- numberKeyEncryptionRounds = kdfEngine.defaultKeyRounds
- memoryUsage = kdfEngine.defaultMemoryUsage
- parallelism = kdfEngine.defaultParallelism
- }
-
- fun getKeyDerivationName(resources: Resources): String {
- return kdfEngine?.getName(resources) ?: ""
- }
-
- fun validatePasswordEncoding(key: String?): Boolean {
- return pwDatabaseV3?.validatePasswordEncoding(key)
- ?: pwDatabaseV4?.validatePasswordEncoding(key)
+ fun validatePasswordEncoding(password: String?, containsKeyFile: Boolean): Boolean {
+ return pwDatabaseV3?.validatePasswordEncoding(password, containsKeyFile)
+ ?: pwDatabaseV4?.validatePasswordEncoding(password, containsKeyFile)
?: false
}
- @Throws(InvalidKeyFileException::class, IOException::class)
+ @Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
fun retrieveMasterKey(key: String?, keyInputStream: InputStream?) {
pwDatabaseV3?.retrieveMasterKey(key, keyInputStream)
pwDatabaseV4?.retrieveMasterKey(key, keyInputStream)
@@ -524,7 +559,7 @@ class Database {
return null
}
- fun getEntryById(id: PwNodeId<*>): EntryVersioned? {
+ fun getEntryById(id: PwNodeId): EntryVersioned? {
pwDatabaseV3?.getEntryById(id)?.let {
return EntryVersioned(it)
}
@@ -535,12 +570,14 @@ class Database {
}
fun getGroupById(id: PwNodeId<*>): GroupVersioned? {
- pwDatabaseV3?.getGroupById(id)?.let {
- return GroupVersioned(it)
- }
- pwDatabaseV4?.getGroupById(id)?.let {
- return GroupVersioned(it)
- }
+ if (id is PwNodeIdInt)
+ pwDatabaseV3?.getGroupById(id)?.let {
+ return GroupVersioned(it)
+ }
+ else if (id is PwNodeIdUUID)
+ pwDatabaseV4?.getGroupById(id)?.let {
+ return GroupVersioned(it)
+ }
return null
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt
index fa4279802..9d5f77fad 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabase.kt
@@ -19,10 +19,10 @@
*/
package com.kunzisoft.keepass.database.element
-import android.util.Log
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
-import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
-import com.kunzisoft.keepass.database.exception.KeyFileEmptyException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseDuplicateUuidException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseKeyFileEmptyException
import com.kunzisoft.keepass.utils.MemoryUtil
import java.io.ByteArrayInputStream
@@ -35,7 +35,11 @@ import java.security.NoSuchAlgorithmException
import java.util.LinkedHashMap
import java.util.UUID
-abstract class PwDatabase, Entry : PwEntry> {
+abstract class PwDatabase<
+ GroupId,
+ Group : PwGroup,
+ Entry : PwEntry
+ > {
// Algorithm used to encrypt the database
protected var algorithm: PwEncryptionAlgorithm? = null
@@ -51,8 +55,10 @@ abstract class PwDatabase, Entry : PwEntry, Group>()
- private var entryIndexes = LinkedHashMap, Entry>()
+ var changeDuplicateId = false
+
+ private var groupIndexes = LinkedHashMap, Group>()
+ private var entryIndexes = LinkedHashMap, Entry>()
abstract val version: String
@@ -72,15 +78,15 @@ abstract class PwDatabase, Entry : PwEntry, Entry : PwEntry, Entry : PwEntry throw KeyFileEmptyException()
+ 0L -> throw LoadDatabaseKeyFileEmptyException()
32L -> return keyData
64L -> try {
return hexStringToByteArray(String(keyData))
@@ -161,15 +167,18 @@ abstract class PwDatabase, Entry : PwEntry, Entry : PwEntry, Entry : PwEntry
+ abstract fun newGroupId(): PwNodeId
- abstract fun newEntryId(): PwNodeId<*>
+ abstract fun newEntryId(): PwNodeId
abstract fun createGroup(): Group
@@ -216,7 +225,7 @@ abstract class PwDatabase, Entry : PwEntry): Boolean {
+ fun isGroupIdUsed(id: PwNodeId): Boolean {
return groupIndexes.containsKey(id)
}
@@ -231,14 +240,21 @@ abstract class PwDatabase, Entry : PwEntry): Group? {
+ fun getGroupById(id: PwNodeId): Group? {
return this.groupIndexes[id]
}
fun addGroupIndex(group: Group) {
val groupId = group.nodeId
if (groupIndexes.containsKey(groupId)) {
- Log.e(TAG, "Error, a group with the same UUID $groupId already exists")
+ if (changeDuplicateId) {
+ val newGroupId = newGroupId()
+ group.nodeId = newGroupId
+ group.parent?.addChildGroup(group)
+ this.groupIndexes[newGroupId] = group
+ } else {
+ throw LoadDatabaseDuplicateUuidException(Type.GROUP, groupId)
+ }
} else {
this.groupIndexes[groupId] = group
}
@@ -258,7 +274,7 @@ abstract class PwDatabase, Entry : PwEntry): Boolean {
+ fun isEntryIdUsed(id: PwNodeId): Boolean {
return entryIndexes.containsKey(id)
}
@@ -266,15 +282,21 @@ abstract class PwDatabase, Entry : PwEntry): Entry? {
+ fun getEntryById(id: PwNodeId): Entry? {
return this.entryIndexes[id]
}
fun addEntryIndex(entry: Entry) {
val entryId = entry.nodeId
if (entryIndexes.containsKey(entryId)) {
- // TODO History
- Log.e(TAG, "Error, a group with the same UUID $entryId already exists, change the UUID")
+ if (changeDuplicateId) {
+ val newEntryId = newEntryId()
+ entry.nodeId = newEntryId
+ entry.parent?.addChildEntry(entry)
+ this.entryIndexes[newEntryId] = entry
+ } else {
+ throw LoadDatabaseDuplicateUuidException(Type.ENTRY, entryId)
+ }
} else {
this.entryIndexes[entryId] = entry
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt
index 4f80d254d..65502af0c 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV3.kt
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element
import com.kunzisoft.keepass.crypto.finalkey.FinalKeyFactory
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
-import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
import com.kunzisoft.keepass.stream.NullOutputStream
import java.io.IOException
import java.io.InputStream
@@ -30,7 +30,7 @@ import java.security.DigestOutputStream
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
-class PwDatabaseV3 : PwDatabase() {
+class PwDatabaseV3 : PwDatabase() {
private var numKeyEncRounds: Int = 0
@@ -112,7 +112,7 @@ class PwDatabaseV3 : PwDatabase() {
return newId
}
- @Throws(InvalidKeyFileException::class, IOException::class)
+ @Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
return if (key != null && keyInputStream != null) {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt
index 377cf0301..eb6c772cc 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/PwDatabaseV4.kt
@@ -27,7 +27,7 @@ import com.kunzisoft.keepass.crypto.CryptoUtil
import com.kunzisoft.keepass.crypto.engine.AesEngine
import com.kunzisoft.keepass.crypto.engine.CipherEngine
import com.kunzisoft.keepass.crypto.keyDerivation.*
-import com.kunzisoft.keepass.database.exception.InvalidKeyFileException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidKeyFileException
import com.kunzisoft.keepass.database.exception.UnknownKDF
import com.kunzisoft.keepass.utils.VariantDictionary
import org.w3c.dom.Node
@@ -40,7 +40,7 @@ import java.util.*
import javax.xml.parsers.DocumentBuilderFactory
-class PwDatabaseV4 : PwDatabase {
+class PwDatabaseV4 : PwDatabase {
var hmacKey: ByteArray? = null
private set
@@ -236,7 +236,7 @@ class PwDatabaseV4 : PwDatabase {
return getCustomData().isNotEmpty()
}
- @Throws(InvalidKeyFileException::class, IOException::class)
+ @Throws(LoadDatabaseInvalidKeyFileException::class, IOException::class)
public override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
var masterKey = byteArrayOf()
@@ -461,10 +461,10 @@ class PwDatabaseV4 : PwDatabase {
return publicCustomData.size() > 0
}
- override fun validatePasswordEncoding(key: String?): Boolean {
- if (key == null)
+ override fun validatePasswordEncoding(password: String?, containsKeyFile: Boolean): Boolean {
+ if (password == null)
return true
- return super.validatePasswordEncoding(key)
+ return super.validatePasswordEncoding(password, containsKeyFile)
}
override fun clearCache() {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.kt
deleted file mode 100644
index 9ad91f6ca..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/ArcFourException.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-class ArcFourException : InvalidDBException() {
- companion object {
- private const val serialVersionUID = 2103983626687861237L
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.kt
deleted file mode 100644
index 2c9f4c3c9..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/ContentFileNotFoundException.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-import android.net.Uri
-import java.io.FileNotFoundException
-
-class ContentFileNotFoundException : FileNotFoundException() {
- companion object {
- fun getInstance(uri: Uri?): FileNotFoundException {
- if (uri == null) {
- return FileNotFoundException()
- }
-
- val scheme = uri.scheme
- return if (scheme != null
- && scheme.isNotEmpty()
- && scheme.equals("content", ignoreCase = true)) {
- ContentFileNotFoundException()
- } else FileNotFoundException()
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseOutputException.kt
similarity index 87%
rename from app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.kt
rename to app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseOutputException.kt
index d892cdee2..d08e82cf4 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/PwDbOutputException.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseOutputException.kt
@@ -19,14 +19,10 @@
*/
package com.kunzisoft.keepass.database.exception
-class PwDbOutputException : Exception {
+class DatabaseOutputException : Exception {
constructor(string: String) : super(string)
constructor(string: String, e: Exception) : super(string, e)
constructor(e: Exception) : super(e)
-
- companion object {
- private const val serialVersionUID = 3321212743159473368L
- }
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.kt
deleted file mode 100644
index 99cc71f2c..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidAlgorithmException.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-class InvalidAlgorithmException : InvalidDBException() {
- companion object {
- private const val serialVersionUID = 3062682891863487208L
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.kt
deleted file mode 100644
index d50721f39..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBException.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-open class InvalidDBException : Exception {
-
- constructor(str: String) : super(str)
-
- constructor() : super()
-
- companion object {
- private const val serialVersionUID = 5191964825154190923L
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.kt
deleted file mode 100644
index 9445755b0..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBSignatureException.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-class InvalidDBSignatureException : InvalidDBException() {
- companion object {
- private const val serialVersionUID = -5358923878743513758L
- }
-
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.kt
deleted file mode 100644
index 577bc0052..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidDBVersionException.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-class InvalidDBVersionException : InvalidDBException() {
- companion object {
- private const val serialVersionUID = -4260650987856400586L
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.kt
deleted file mode 100644
index d7f54fcec..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidKeyFileException.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */package com.kunzisoft.keepass.database.exception
-
-open class InvalidKeyFileException : InvalidDBException() {
- companion object {
- private const val serialVersionUID = 5540694419562294464L
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.kt
deleted file mode 100644
index dcd51b94d..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/InvalidPasswordException.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-class InvalidPasswordException : InvalidDBException() {
- companion object {
- private const val serialVersionUID = -8729476180242058319L
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.kt
deleted file mode 100644
index 474d2dfea..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/KeyFileEmptyException.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright 2019 Jeremy Jamet / Kunzisoft.
- *
- * This file is part of KeePass DX.
- *
- * KeePass DX is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeePass DX is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeePass DX. If not, see .
- *
- */
-package com.kunzisoft.keepass.database.exception
-
-class KeyFileEmptyException : InvalidKeyFileException() {
- companion object {
- private const val serialVersionUID = -1630780661204212325L
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/LoadDatabaseException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/LoadDatabaseException.kt
new file mode 100644
index 000000000..4f9e2025a
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/LoadDatabaseException.kt
@@ -0,0 +1,75 @@
+package com.kunzisoft.keepass.database.exception
+
+import android.content.res.Resources
+import androidx.annotation.StringRes
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.database.element.PwNodeId
+import com.kunzisoft.keepass.database.element.Type
+import java.io.IOException
+
+
+class LoadDatabaseArcFourException :
+ LoadDatabaseException(R.string.error_arc4)
+
+class LoadDatabaseFileNotFoundException :
+ LoadDatabaseException(R.string.file_not_found_content)
+
+class LoadDatabaseInvalidAlgorithmException :
+ LoadDatabaseException(R.string.invalid_algorithm)
+
+class LoadDatabaseDuplicateUuidException(type: Type, uuid: PwNodeId<*>):
+ LoadDatabaseException(R.string.invalid_db_same_uuid, type.name, uuid.toString())
+
+class LoadDatabaseIOException(exception: IOException) :
+ LoadDatabaseException(exception, R.string.error_load_database)
+
+class LoadDatabaseKDFMemoryException(exception: IOException) :
+ LoadDatabaseException(exception, R.string.error_load_database_KDF_memory)
+
+class LoadDatabaseSignatureException :
+ LoadDatabaseException(R.string.invalid_db_sig)
+
+class LoadDatabaseVersionException :
+ LoadDatabaseException(R.string.unsupported_db_version)
+
+open class LoadDatabaseInvalidKeyFileException :
+ LoadDatabaseException(R.string.keyfile_does_not_exist)
+
+class LoadDatabaseInvalidPasswordException :
+ LoadDatabaseException(R.string.invalid_password)
+
+class LoadDatabaseKeyFileEmptyException :
+ LoadDatabaseException(R.string.keyfile_is_empty)
+
+class LoadDatabaseNoMemoryException(exception: OutOfMemoryError) :
+ LoadDatabaseException(exception, R.string.error_out_of_memory)
+
+open class LoadDatabaseException : Exception {
+
+ @StringRes
+ var errorId: Int = R.string.error_load_database
+ var parameters: (Array)? = null
+
+ constructor(errorMessageId: Int) : super() {
+ errorId = errorMessageId
+ }
+
+ constructor(errorMessageId: Int, vararg params: String) : super() {
+ errorId = errorMessageId
+ parameters = params
+ }
+
+ constructor(throwable: Throwable, errorMessageId: Int? = null) : super(throwable) {
+ errorMessageId?.let {
+ errorId = it
+ }
+ }
+
+ constructor() : super()
+
+ fun getLocalizedMessage(resources: Resources): String {
+ parameters?.let {
+ return resources.getString(errorId, *it)
+ } ?: return resources.getString(errorId)
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.kt
index 8db01ab9c..0c3f13a8d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/SamsungClipboardException.kt
@@ -19,9 +19,4 @@
*/
package com.kunzisoft.keepass.database.exception
-class SamsungClipboardException(e: Exception) : Exception(e) {
- companion object {
- private const val serialVersionUID = -3168837280393843509L
- }
-
-}
+class SamsungClipboardException(e: Exception) : Exception(e)
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt
index 2897148c5..435bf4b48 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/UnknownKDF.kt
@@ -2,8 +2,4 @@ package com.kunzisoft.keepass.database.exception
import java.io.IOException
-class UnknownKDF : IOException(message) {
- companion object {
- private const val message = "Unknown key derivation function"
- }
-}
+class UnknownKDF : IOException("Unknown key derivation function")
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt
index 9edbfb354..9cff24a2a 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/PwDbHeaderV4.kt
@@ -25,7 +25,7 @@ import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters
import com.kunzisoft.keepass.database.NodeHandler
import com.kunzisoft.keepass.database.element.*
-import com.kunzisoft.keepass.database.exception.InvalidDBVersionException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseVersionException
import com.kunzisoft.keepass.stream.CopyInputStream
import com.kunzisoft.keepass.stream.HmacBlockStream
import com.kunzisoft.keepass.stream.LEDataInputStream
@@ -130,9 +130,9 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
/** Assumes the input stream is at the beginning of the .kdbx file
* @param inputStream
* @throws IOException
- * @throws InvalidDBVersionException
+ * @throws LoadDatabaseVersionException
*/
- @Throws(IOException::class, InvalidDBVersionException::class)
+ @Throws(IOException::class, LoadDatabaseVersionException::class)
fun loadFromFile(inputStream: InputStream): HeaderAndHash {
val messageDigest: MessageDigest
try {
@@ -150,12 +150,12 @@ class PwDbHeaderV4(private val databaseV4: PwDatabaseV4) : PwDbHeader() {
val sig2 = littleEndianDataInputStream.readInt()
if (!matchesHeader(sig1, sig2)) {
- throw InvalidDBVersionException()
+ throw LoadDatabaseVersionException()
}
version = littleEndianDataInputStream.readUInt() // Erase previous value
if (!validVersion(version)) {
- throw InvalidDBVersionException()
+ throw LoadDatabaseVersionException()
}
var done = false
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/Importer.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/Importer.kt
index 1eec9700e..83bd0bb55 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/Importer.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/Importer.kt
@@ -20,13 +20,13 @@
package com.kunzisoft.keepass.database.file.load
import com.kunzisoft.keepass.database.element.PwDatabase
-import com.kunzisoft.keepass.database.exception.InvalidDBException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseException
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import java.io.IOException
import java.io.InputStream
-abstract class Importer> {
+abstract class Importer> {
/**
* Load a versioned database file, return contents in a new PwDatabase.
@@ -36,9 +36,9 @@ abstract class Importer> {
* @return new PwDatabase container.
*
* @throws IOException on any file error.
- * @throws InvalidDBException on database error.
+ * @throws LoadDatabaseException on database error.
*/
- @Throws(IOException::class, InvalidDBException::class)
+ @Throws(IOException::class, LoadDatabaseException::class)
abstract fun openDatabase(databaseInputStream: InputStream,
password: String?,
keyInputStream: InputStream?,
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt
index 3771f2d09..c16c24281 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV3.kt
@@ -73,7 +73,7 @@ class ImporterV3 : Importer() {
private lateinit var mDatabaseToOpen: PwDatabaseV3
- @Throws(IOException::class, InvalidDBException::class)
+ @Throws(IOException::class, LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
password: String?,
keyInputStream: InputStream?,
@@ -92,11 +92,11 @@ class ImporterV3 : Importer() {
hdr.loadFromFile(filebuf, 0)
if (hdr.signature1 != PwDbHeader.PWM_DBSIG_1 || hdr.signature2 != PwDbHeaderV3.DBSIG_2) {
- throw InvalidDBSignatureException()
+ throw LoadDatabaseSignatureException()
}
if (!hdr.matchesVersion()) {
- throw InvalidDBVersionException()
+ throw LoadDatabaseVersionException()
}
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
@@ -109,7 +109,7 @@ class ImporterV3 : Importer() {
} else if (hdr.flags and PwDbHeaderV3.FLAG_TWOFISH != 0) {
mDatabaseToOpen.encryptionAlgorithm = PwEncryptionAlgorithm.Twofish
} else {
- throw InvalidAlgorithmException()
+ throw LoadDatabaseInvalidAlgorithmException()
}
mDatabaseToOpen.numberKeyEncryptionRounds = hdr.numKeyEncRounds.toLong()
@@ -152,7 +152,7 @@ class ImporterV3 : Importer() {
} catch (e1: IllegalBlockSizeException) {
throw IOException("Invalid block size")
} catch (e1: BadPaddingException) {
- throw InvalidPasswordException()
+ throw LoadDatabaseInvalidPasswordException()
}
val md: MessageDigest
@@ -171,7 +171,7 @@ class ImporterV3 : Importer() {
if (!Arrays.equals(hash, hdr.contentsHash)) {
Log.w(TAG, "Database file did not decrypt correctly. (checksum code is broken)")
- throw InvalidPasswordException()
+ throw LoadDatabaseInvalidPasswordException()
}
// New manual root because V3 contains multiple root groups (here available with getRootGroups())
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt
index d75237721..e10974c59 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/load/ImporterV4.kt
@@ -26,9 +26,9 @@ import com.kunzisoft.keepass.crypto.StreamCipherFactory
import com.kunzisoft.keepass.crypto.engine.CipherEngine
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
import com.kunzisoft.keepass.database.element.*
-import com.kunzisoft.keepass.database.exception.ArcFourException
-import com.kunzisoft.keepass.database.exception.InvalidDBException
-import com.kunzisoft.keepass.database.exception.InvalidPasswordException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseArcFourException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseException
+import com.kunzisoft.keepass.database.exception.LoadDatabaseInvalidPasswordException
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
import com.kunzisoft.keepass.database.element.security.ProtectedBinary
import com.kunzisoft.keepass.database.element.security.ProtectedString
@@ -56,7 +56,8 @@ import javax.crypto.Cipher
import javax.crypto.NoSuchPaddingException
import kotlin.math.min
-class ImporterV4(private val streamDir: File) : Importer() {
+class ImporterV4(private val streamDir: File,
+ private val fixDuplicateUUID: Boolean = false) : Importer() {
private var randomStream: StreamCipher? = null
private lateinit var mDatabase: PwDatabaseV4
@@ -89,7 +90,7 @@ class ImporterV4(private val streamDir: File) : Importer() {
private var entryCustomDataKey: String? = null
private var entryCustomDataValue: String? = null
- @Throws(IOException::class, InvalidDBException::class)
+ @Throws(IOException::class, LoadDatabaseException::class)
override fun openDatabase(databaseInputStream: InputStream,
password: String?,
keyInputStream: InputStream?,
@@ -99,6 +100,9 @@ class ImporterV4(private val streamDir: File) : Importer() {
progressTaskUpdater?.updateMessage(R.string.retrieving_db_key)
mDatabase = PwDatabaseV4()
+
+ mDatabase.changeDuplicateId = fixDuplicateUUID
+
val header = PwDbHeaderV4(mDatabase)
val headerAndHash = header.loadFromFile(databaseInputStream)
@@ -138,14 +142,14 @@ class ImporterV4(private val streamDir: File) : Importer() {
try {
storedStartBytes = dataDecrypted.readBytes(32)
if (storedStartBytes == null || storedStartBytes.size != 32) {
- throw InvalidPasswordException()
+ throw LoadDatabaseInvalidPasswordException()
}
} catch (e: IOException) {
- throw InvalidPasswordException()
+ throw LoadDatabaseInvalidPasswordException()
}
if (!Arrays.equals(storedStartBytes, header.streamStartBytes)) {
- throw InvalidPasswordException()
+ throw LoadDatabaseInvalidPasswordException()
}
isPlain = HashedBlockInputStream(dataDecrypted)
@@ -153,18 +157,18 @@ class ImporterV4(private val streamDir: File) : Importer() {
val isData = LEDataInputStream(databaseInputStream)
val storedHash = isData.readBytes(32)
if (!Arrays.equals(storedHash, hashOfHeader)) {
- throw InvalidDBException()
+ throw LoadDatabaseException()
}
- val hmacKey = mDatabase.hmacKey ?: throw InvalidDBException()
+ val hmacKey = mDatabase.hmacKey ?: throw LoadDatabaseException()
val headerHmac = PwDbHeaderV4.computeHeaderHmac(pbHeader, hmacKey)
val storedHmac = isData.readBytes(32)
if (storedHmac == null || storedHmac.size != 32) {
- throw InvalidDBException()
+ throw LoadDatabaseException()
}
// Mac doesn't match
if (!Arrays.equals(headerHmac, storedHmac)) {
- throw InvalidDBException()
+ throw LoadDatabaseException()
}
val hmIs = HmacBlockInputStream(isData, true, hmacKey)
@@ -185,7 +189,7 @@ class ImporterV4(private val streamDir: File) : Importer() {
randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
if (randomStream == null) {
- throw ArcFourException()
+ throw LoadDatabaseArcFourException()
}
readXmlStreamed(isXml)
@@ -270,7 +274,7 @@ class ImporterV4(private val streamDir: File) : Importer() {
Binaries
}
- @Throws(IOException::class, InvalidDBException::class)
+ @Throws(IOException::class, LoadDatabaseException::class)
private fun readXmlStreamed(readerStream: InputStream) {
try {
readDocumentStreamed(createPullParser(readerStream))
@@ -281,7 +285,7 @@ class ImporterV4(private val streamDir: File) : Importer() {
}
- @Throws(XmlPullParserException::class, IOException::class, InvalidDBException::class)
+ @Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
private fun readDocumentStreamed(xpp: XmlPullParser) {
ctxGroups.clear()
@@ -312,7 +316,7 @@ class ImporterV4(private val streamDir: File) : Importer() {
if (ctxGroups.size != 0) throw IOException("Malformed")
}
- @Throws(XmlPullParserException::class, IOException::class, InvalidDBException::class)
+ @Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
private fun readXmlElement(ctx: KdbContext, xpp: XmlPullParser): KdbContext {
val name = xpp.name
when (ctx) {
@@ -336,7 +340,7 @@ class ImporterV4(private val streamDir: File) : Importer() {
if (encodedHash.isNotEmpty() && hashOfHeader != null) {
val hash = Base64Coder.decode(encodedHash)
if (!Arrays.equals(hash, hashOfHeader)) {
- throw InvalidDBException()
+ throw LoadDatabaseException()
}
}
} else if (name.equals(PwDatabaseV4XML.ElemSettingsChanged, ignoreCase = true)) {
@@ -354,7 +358,6 @@ class ImporterV4(private val streamDir: File) : Importer() {
} else if (name.equals(PwDatabaseV4XML.ElemDbDefaultUserChanged, ignoreCase = true)) {
mDatabase.defaultUserNameChanged = readPwTime(xpp)
} else if (name.equals(PwDatabaseV4XML.ElemDbColor, ignoreCase = true)) {
- // TODO: Add support to interpret the color if we want to allow changing the database color
mDatabase.color = readString(xpp)
} else if (name.equals(PwDatabaseV4XML.ElemDbMntncHistoryDays, ignoreCase = true)) {
mDatabase.maintenanceHistoryDays = readUInt(xpp, DEFAULT_HISTORY_DAYS)
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt
index 3ebadb9f5..6a5720825 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbHeaderOutputV4.kt
@@ -24,7 +24,7 @@ import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters
import com.kunzisoft.keepass.database.element.PwDatabaseV4
import com.kunzisoft.keepass.database.file.PwDbHeader
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
-import com.kunzisoft.keepass.database.exception.PwDbOutputException
+import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.stream.HmacBlockStream
import com.kunzisoft.keepass.stream.LEDataOutputStream
import com.kunzisoft.keepass.stream.MacOutputStream
@@ -41,7 +41,7 @@ import java.security.NoSuchAlgorithmException
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
-class PwDbHeaderOutputV4 @Throws(PwDbOutputException::class)
+class PwDbHeaderOutputV4 @Throws(DatabaseOutputException::class)
constructor(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os: OutputStream) : PwDbHeaderOutput() {
private val los: LEDataOutputStream
private val mos: MacOutputStream
@@ -54,13 +54,13 @@ constructor(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os:
try {
md = MessageDigest.getInstance("SHA-256")
} catch (e: NoSuchAlgorithmException) {
- throw PwDbOutputException("SHA-256 not implemented here.")
+ throw DatabaseOutputException("SHA-256 not implemented here.")
}
try {
db.makeFinalKey(header.masterSeed)
} catch (e: IOException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
}
val hmac: Mac
@@ -69,9 +69,9 @@ constructor(private val db: PwDatabaseV4, private val header: PwDbHeaderV4, os:
val signingKey = SecretKeySpec(HmacBlockStream.GetHmacKey64(db.hmacKey, Types.ULONG_MAX_VALUE), "HmacSHA256")
hmac.init(signingKey)
} catch (e: NoSuchAlgorithmException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
} catch (e: InvalidKeyException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
}
dos = DigestOutputStream(os, md)
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt
index e9a486af6..a670182ca 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbOutput.kt
@@ -20,7 +20,7 @@
package com.kunzisoft.keepass.database.file.save
import com.kunzisoft.keepass.database.file.PwDbHeader
-import com.kunzisoft.keepass.database.exception.PwDbOutputException
+import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import java.io.OutputStream
import java.security.NoSuchAlgorithmException
@@ -28,13 +28,13 @@ import java.security.SecureRandom
abstract class PwDbOutput protected constructor(protected var mOS: OutputStream) {
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
protected open fun setIVs(header: Header): SecureRandom {
val random: SecureRandom
try {
random = SecureRandom.getInstance("SHA1PRNG")
} catch (e: NoSuchAlgorithmException) {
- throw PwDbOutputException("Does not support secure random number generation.")
+ throw DatabaseOutputException("Does not support secure random number generation.")
}
random.nextBytes(header.encryptionIV)
@@ -43,10 +43,10 @@ abstract class PwDbOutput protected constructor(protected v
return random
}
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
abstract fun output()
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
abstract fun outputHeader(outputStream: OutputStream): Header
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt
index 8a546c298..5a2710cbe 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV3Output.kt
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.database.file.save
import com.kunzisoft.keepass.crypto.CipherFactory
import com.kunzisoft.keepass.database.element.*
-import com.kunzisoft.keepass.database.exception.PwDbOutputException
+import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.database.file.PwDbHeader
import com.kunzisoft.keepass.database.file.PwDbHeaderV3
import com.kunzisoft.keepass.stream.LEDataOutputStream
@@ -42,18 +42,18 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
private var headerHashBlock: ByteArray? = null
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
fun getFinalKey(header: PwDbHeader): ByteArray? {
try {
val h3 = header as PwDbHeaderV3
mDatabaseV3.makeFinalKey(h3.masterSeed, h3.transformSeed, mDatabaseV3.numberKeyEncryptionRounds)
return mDatabaseV3.finalKey
} catch (e: IOException) {
- throw PwDbOutputException("Key creation failed.", e)
+ throw DatabaseOutputException("Key creation failed.", e)
}
}
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
override fun output() {
// Before we output the header, we should sort our list of groups
// and remove any orphaned nodes that are no longer part of the tree hierarchy
@@ -74,7 +74,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
throw Exception()
}
} catch (e: Exception) {
- throw PwDbOutputException("Algorithm not supported.", e)
+ throw DatabaseOutputException("Algorithm not supported.", e)
}
try {
@@ -86,23 +86,23 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
bos.close()
} catch (e: InvalidKeyException) {
- throw PwDbOutputException("Invalid key", e)
+ throw DatabaseOutputException("Invalid key", e)
} catch (e: InvalidAlgorithmParameterException) {
- throw PwDbOutputException("Invalid algorithm parameter.", e)
+ throw DatabaseOutputException("Invalid algorithm parameter.", e)
} catch (e: IOException) {
- throw PwDbOutputException("Failed to output final encrypted part.", e)
+ throw DatabaseOutputException("Failed to output final encrypted part.", e)
}
}
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
override fun setIVs(header: PwDbHeaderV3): SecureRandom {
val random = super.setIVs(header)
random.nextBytes(header.transformSeed)
return random
}
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
override fun outputHeader(outputStream: OutputStream): PwDbHeaderV3 {
// Build header
val header = PwDbHeaderV3()
@@ -115,7 +115,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
} else if (mDatabaseV3.encryptionAlgorithm === PwEncryptionAlgorithm.Twofish) {
header.flags = header.flags or PwDbHeaderV3.FLAG_TWOFISH
} else {
- throw PwDbOutputException("Unsupported algorithm.")
+ throw DatabaseOutputException("Unsupported algorithm.")
}
header.version = PwDbHeaderV3.DBVER_DW
@@ -130,7 +130,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
try {
messageDigest = MessageDigest.getInstance("SHA-256")
} catch (e: NoSuchAlgorithmException) {
- throw PwDbOutputException("SHA-256 not implemented here.", e)
+ throw DatabaseOutputException("SHA-256 not implemented here.", e)
}
// Header checksum
@@ -138,7 +138,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
try {
headerDigest = MessageDigest.getInstance("SHA-256")
} catch (e: NoSuchAlgorithmException) {
- throw PwDbOutputException("SHA-256 not implemented here.", e)
+ throw DatabaseOutputException("SHA-256 not implemented here.", e)
}
var nos = NullOutputStream()
@@ -151,7 +151,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
pho.outputEnd()
headerDos.flush()
} catch (e: IOException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
}
val headerHash = headerDigest.digest()
@@ -166,7 +166,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
bos.flush()
bos.close()
} catch (e: IOException) {
- throw PwDbOutputException("Failed to generate checksum.", e)
+ throw DatabaseOutputException("Failed to generate checksum.", e)
}
header.contentsHash = messageDigest!!.digest()
@@ -181,14 +181,14 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
pho.outputEnd()
dos.flush()
} catch (e: IOException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
}
return header
}
@Suppress("CAST_NEVER_SUCCEEDS")
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
fun outputPlanGroupAndEntries(os: OutputStream) {
val los = LEDataOutputStream(os)
@@ -199,7 +199,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
los.writeInt(headerHashBlock!!.size)
los.write(headerHashBlock!!)
} catch (e: IOException) {
- throw PwDbOutputException("Failed to output header hash.", e)
+ throw DatabaseOutputException("Failed to output header hash.", e)
}
}
@@ -209,7 +209,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
try {
pgo.output()
} catch (e: IOException) {
- throw PwDbOutputException("Failed to output a tree", e)
+ throw DatabaseOutputException("Failed to output a tree", e)
}
}
mDatabaseV3.doForEachEntryInIndex { entry ->
@@ -217,7 +217,7 @@ class PwDbV3Output(private val mDatabaseV3: PwDatabaseV3, os: OutputStream) : Pw
try {
peo.output()
} catch (e: IOException) {
- throw PwDbOutputException("Failed to output an entry.", e)
+ throw DatabaseOutputException("Failed to output an entry.", e)
}
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt
index bb662366d..c2a426966 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/save/PwDbV4Output.kt
@@ -29,7 +29,7 @@ import com.kunzisoft.keepass.crypto.engine.CipherEngine
import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory
import com.kunzisoft.keepass.database.*
import com.kunzisoft.keepass.database.element.*
-import com.kunzisoft.keepass.database.exception.PwDbOutputException
+import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.database.exception.UnknownKDF
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
import com.kunzisoft.keepass.database.file.PwDbHeaderV4
@@ -63,14 +63,14 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
private var headerHmac: ByteArray? = null
private var engine: CipherEngine? = null
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
override fun output() {
try {
try {
engine = CipherFactory.getInstance(mDatabaseV4.dataCipher)
} catch (e: NoSuchAlgorithmException) {
- throw PwDbOutputException("No such cipher", e)
+ throw DatabaseOutputException("No such cipher", e)
}
header = outputHeader(mOS)
@@ -104,13 +104,13 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
outputDatabase(osXml)
osXml.close()
} catch (e: IllegalArgumentException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
} catch (e: IllegalStateException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
}
} catch (e: IOException) {
- throw PwDbOutputException(e)
+ throw DatabaseOutputException(e)
}
}
@@ -228,7 +228,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
xml.endTag(null, PwDatabaseV4XML.ElemMeta)
}
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
private fun attachStreamEncryptor(header: PwDbHeaderV4, os: OutputStream): CipherOutputStream {
val cipher: Cipher
try {
@@ -236,13 +236,13 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
cipher = engine!!.getCipher(Cipher.ENCRYPT_MODE, mDatabaseV4.finalKey!!, header.encryptionIV)
} catch (e: Exception) {
- throw PwDbOutputException("Invalid algorithm.", e)
+ throw DatabaseOutputException("Invalid algorithm.", e)
}
return CipherOutputStream(os, cipher)
}
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
override fun setIVs(header: PwDbHeaderV4): SecureRandom {
val random = super.setIVs(header)
random.nextBytes(header.masterSeed)
@@ -275,7 +275,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
randomStream = StreamCipherFactory.getInstance(header.innerRandomStream, header.innerRandomStreamKey)
if (randomStream == null) {
- throw PwDbOutputException("Invalid random cipher")
+ throw DatabaseOutputException("Invalid random cipher")
}
if (header.version < PwDbHeaderV4.FILE_VERSION_32_4) {
@@ -285,7 +285,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
return random
}
- @Throws(PwDbOutputException::class)
+ @Throws(DatabaseOutputException::class)
override fun outputHeader(outputStream: OutputStream): PwDbHeaderV4 {
val header = PwDbHeaderV4(mDatabaseV4)
@@ -295,7 +295,7 @@ class PwDbV4Output(private val mDatabaseV4: PwDatabaseV4, outputStream: OutputSt
try {
pho.output()
} catch (e: IOException) {
- throw PwDbOutputException("Failed to output the header.", e)
+ throw DatabaseOutputException("Failed to output the header.", e)
}
hashOfHeader = pho.hashOfHeader
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt
index aeb2fe35e..04390d4e5 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt
@@ -22,18 +22,23 @@ package com.kunzisoft.keepass.settings
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.res.Resources
+import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
-import androidx.annotation.RequiresApi
-import androidx.fragment.app.DialogFragment
-import androidx.appcompat.app.AlertDialog
import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
import android.view.autofill.AutofillManager
import android.widget.Toast
+import androidx.annotation.RequiresApi
+import androidx.appcompat.app.AlertDialog
import androidx.biometric.BiometricManager
+import androidx.fragment.app.DialogFragment
import androidx.preference.*
+import com.kunzisoft.androidclearchroma.ChromaUtil
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.*
@@ -41,12 +46,13 @@ import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.stylish.Stylish
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
-import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.biometric.BiometricUnlockDatabaseHelper
+import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
+import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.settings.preference.*
+import com.kunzisoft.keepass.settings.preference.DialogColorPreference.Companion.DISABLE_COLOR
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
@@ -56,6 +62,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
private var mCount = 0
+ private var dbCustomColorPref: DialogColorPreference? = null
private var mRoundPref: InputKdfNumberPreference? = null
private var mMemoryPref: InputKdfNumberPreference? = null
private var mParallelismPref: InputKdfNumberPreference? = null
@@ -339,11 +346,11 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
if (mDatabase.loaded) {
- val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_general_key))
+ val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_general_key))
- // Db name
+ // Database name
val dbNamePref: InputTextPreference? = findPreference(getString(R.string.database_name_key))
- if (mDatabase.containsName()) {
+ if (mDatabase.allowName) {
dbNamePref?.summary = mDatabase.name
} else {
dbGeneralPrefCategory?.removePreference(dbNamePref)
@@ -351,32 +358,66 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
// Database description
val dbDescriptionPref: InputTextPreference? = findPreference(getString(R.string.database_description_key))
- if (mDatabase.containsDescription()) {
+ if (mDatabase.allowDescription) {
dbDescriptionPref?.summary = mDatabase.description
} else {
dbGeneralPrefCategory?.removePreference(dbDescriptionPref)
}
- // Database compression
- findPreference(getString(R.string.database_data_compression_key))
- ?.summary = (mDatabase.compressionAlgorithm ?: PwCompressionAlgorithm.None).getName(resources)
-
- // Recycle bin
- val recycleBinPref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_key))
- // TODO Recycle
- dbGeneralPrefCategory?.removePreference(recycleBinPref) // To delete
- if (mDatabase.isRecycleBinAvailable) {
- recycleBinPref?.isChecked = mDatabase.isRecycleBinEnabled
- recycleBinPref?.isEnabled = false
+ // Database default username
+ val dbDefaultUsername: InputTextPreference? = findPreference(getString(R.string.database_default_username_key))
+ if (mDatabase.allowDefaultUsername) {
+ dbDefaultUsername?.summary = mDatabase.defaultUsername
} else {
- dbGeneralPrefCategory?.removePreference(recycleBinPref)
+ dbDefaultUsername?.isEnabled = false
+ // TODO dbGeneralPrefCategory?.removePreference(dbDefaultUsername)
+ }
+
+ // Database custom color
+ dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key))
+ if (mDatabase.allowCustomColor) {
+ dbCustomColorPref?.apply {
+ try {
+ color = Color.parseColor(mDatabase.customColor)
+ summary = mDatabase.customColor
+ } catch (e: Exception) {
+ color = DISABLE_COLOR
+ summary = ""
+ }
+ }
+ } else {
+ dbCustomColorPref?.isEnabled = false
+ // TODO dbGeneralPrefCategory?.removePreference(dbCustomColorPref)
}
// Version
findPreference(getString(R.string.database_version_key))
- ?.summary = mDatabase.getVersion()
+ ?.summary = mDatabase.version
- findPreference(getString(R.string.database_history_key))
+ val dbCompressionPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_compression_key))
+
+ // Database compression
+ val databaseDataCompressionPref = findPreference(getString(R.string.database_data_compression_key))
+ if (mDatabase.allowDataCompression) {
+ databaseDataCompressionPref?.summary = (mDatabase.compressionAlgorithm
+ ?: PwCompressionAlgorithm.None).getName(resources)
+ } else {
+ dbCompressionPrefCategory?.isVisible = false
+ }
+
+ val dbRecycleBinPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_recycle_bin_key))
+
+ // Recycle bin
+ val recycleBinPref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_key))
+ if (mDatabase.allowRecycleBin) {
+ recycleBinPref?.isChecked = mDatabase.isRecycleBinEnabled
+ // TODO Recycle Bin
+ recycleBinPref?.isEnabled = false
+ } else {
+ dbRecycleBinPrefCategory?.isVisible = false
+ }
+
+ findPreference(getString(R.string.database_category_history_key))
?.isVisible = mDatabase.manageHistory == true
// Max history items
@@ -427,7 +468,8 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
findPreference(getString(R.string.settings_database_change_credentials_key))?.apply {
onPreferenceClickListener = Preference.OnPreferenceClickListener {
fragmentManager?.let { fragmentManager ->
- AssignMasterKeyDialogFragment().show(fragmentManager, "passwordDialog")
+ AssignMasterKeyDialogFragment.getInstance(mDatabase.allowNoMasterKey)
+ .show(fragmentManager, "passwordDialog")
}
false
}
@@ -488,6 +530,27 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
}
}
+ private val colorSelectedListener: ((Boolean, Int)-> Unit)? = { enable, color ->
+ dbCustomColorPref?.summary = ChromaUtil.getFormattedColorString(color, false)
+ if (enable) {
+ dbCustomColorPref?.color = color
+ } else {
+ dbCustomColorPref?.color = DISABLE_COLOR
+ }
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = super.onCreateView(inflater, container, savedInstanceState)
+
+ try {
+ // To reassign color listener after orientation change
+ val chromaDialog = fragmentManager?.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
+ chromaDialog?.onColorSelectedListener = colorSelectedListener
+ } catch (e: Exception) {}
+
+ return view
+ }
+
override fun onDisplayPreferenceDialog(preference: Preference?) {
var otherDialogFragment = false
@@ -502,6 +565,14 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
preference.key == getString(R.string.database_description_key) -> {
dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.key)
}
+ preference.key == getString(R.string.database_default_username_key) -> {
+ dialogFragment = DatabaseDefaultUsernamePreferenceDialogFragmentCompat.newInstance(preference.key)
+ }
+ preference.key == getString(R.string.database_custom_color_key) -> {
+ dialogFragment = DatabaseColorPreferenceDialogFragmentCompat.newInstance(preference.key).apply {
+ onColorSelectedListener = colorSelectedListener
+ }
+ }
preference.key == getString(R.string.database_data_compression_key) -> {
dialogFragment = DatabaseDataCompressionPreferenceDialogFragmentCompat.newInstance(preference.key)
}
@@ -536,7 +607,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
if (dialogFragment != null && !mDatabaseReadOnly) {
dialogFragment.setTargetFragment(this, 0)
- dialogFragment.show(fragmentManager, null)
+ dialogFragment.show(fragmentManager, TAG_PREF_FRAGMENT)
}
// Could not be handled here. Try with the super method.
else if (otherDialogFragment) {
@@ -560,6 +631,8 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
private const val TAG_KEY = "NESTED_KEY"
+ private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
+
private const val REQUEST_CODE_AUTOFILL = 5201
@JvmOverloads
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt
index 93c7e7323..46222da3b 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt
@@ -33,6 +33,12 @@ object PreferencesUtil {
return prefs.getBoolean(context.getString(R.string.show_read_only_warning), true)
}
+ fun rememberKeyFiles(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.keyfile_key),
+ context.resources.getBoolean(R.bool.keyfile_default))
+ }
+
fun omitBackup(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.omitbackup_key),
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt
index 5f8897212..e28b073b9 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt
@@ -115,7 +115,7 @@ open class SettingsActivity
true)
}
// Show the progress dialog now or after dialog confirmation
- if (database.validatePasswordEncoding(masterPassword)) {
+ if (database.validatePasswordEncoding(masterPassword, keyFileChecked)) {
progressDialogThread.start()
} else {
PasswordEncodingDialogFragment().apply {
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogColorPreference.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogColorPreference.kt
new file mode 100644
index 000000000..a94fc8912
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preference/DialogColorPreference.kt
@@ -0,0 +1,33 @@
+package com.kunzisoft.keepass.settings.preference
+
+import android.content.Context
+import android.graphics.Color
+import android.util.AttributeSet
+import androidx.annotation.ColorInt
+import com.kunzisoft.androidclearchroma.ChromaPreferenceCompat
+
+import com.kunzisoft.keepass.R
+
+class DialogColorPreference @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = R.attr.dialogPreferenceStyle,
+ defStyleRes: Int = defStyleAttr)
+ : ChromaPreferenceCompat(context, attrs, defStyleAttr, defStyleRes) {
+
+ override fun setSummary(summary: CharSequence?) {
+ if (color == DISABLE_COLOR)
+ super.setSummary("")
+ else
+ super.setSummary(summary)
+ }
+
+ override fun getDialogLayoutResource(): Int {
+ return R.layout.pref_dialog_input_color
+ }
+
+ companion object {
+
+ @ColorInt
+ const val DISABLE_COLOR: Int = Color.TRANSPARENT
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseColorPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseColorPreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..1c737f114
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseColorPreferenceDialogFragmentCompat.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeePass DX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.app.Dialog
+import android.graphics.Color
+import android.os.Bundle
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.widget.CompoundButton
+import androidx.annotation.ColorInt
+import androidx.appcompat.app.AlertDialog
+import com.kunzisoft.androidclearchroma.ChromaUtil
+import com.kunzisoft.androidclearchroma.IndicatorMode
+import com.kunzisoft.androidclearchroma.colormode.ColorMode
+import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment
+import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment.*
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.tasks.ActionRunnable
+import java.lang.Exception
+
+class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
+
+ private lateinit var rootView: View
+ private lateinit var enableSwitchView: CompoundButton
+ private var chromaColorFragment: ChromaColorFragment? = null
+
+ var onColorSelectedListener: ((enable: Boolean, color: Int) -> Unit)? = null
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+
+ val alertDialogBuilder = AlertDialog.Builder(activity!!)
+
+ rootView = activity!!.layoutInflater.inflate(R.layout.pref_dialog_input_color, null)
+ enableSwitchView = rootView.findViewById(R.id.switch_element)
+
+ val fragmentManager = childFragmentManager
+ chromaColorFragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_COLORS) as ChromaColorFragment?
+ val fragmentTransaction = fragmentManager.beginTransaction()
+
+ database?.let { database ->
+ val initColor = try {
+ enableSwitchView.isChecked = true
+ Color.parseColor(database.customColor)
+ } catch (e: Exception) {
+ enableSwitchView.isChecked = false
+ DEFAULT_COLOR
+ }
+ arguments?.putInt(ARG_INITIAL_COLOR, initColor)
+ }
+
+ if (chromaColorFragment == null) {
+ chromaColorFragment = newInstance(arguments)
+ fragmentTransaction.add(com.kunzisoft.androidclearchroma.R.id.color_dialog_container, chromaColorFragment!!, TAG_FRAGMENT_COLORS).commit()
+ }
+
+ alertDialogBuilder.setPositiveButton(android.R.string.ok) { _, _ ->
+ val currentColor = chromaColorFragment!!.currentColor
+ val customColorEnable = enableSwitchView.isChecked
+
+ onColorSelectedListener?.invoke(customColorEnable, currentColor)
+
+ database?.let { database ->
+ val newColor = if (customColorEnable) {
+ ChromaUtil.getFormattedColorString(currentColor, false)
+ } else {
+ ""
+ }
+ val oldColor = database.customColor
+ database.customColor = newColor
+
+ actionInUIThreadAfterSaveDatabase = AfterColorSave(newColor, oldColor)
+ }
+
+ super.onDialogClosed(true)
+ dismiss()
+ }
+
+ alertDialogBuilder.setNegativeButton(android.R.string.cancel) { _, _ ->
+ super.onDialogClosed(false)
+ dismiss()
+ }
+
+ alertDialogBuilder.setView(rootView)
+
+ val dialog = alertDialogBuilder.create()
+ // request a window without the title
+ dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
+
+ dialog.setOnShowListener { measureLayout(it as Dialog) }
+
+ return dialog
+ }
+
+ /**
+ * Set new dimensions to dialog
+ * @param ad dialog
+ */
+ private fun measureLayout(ad: Dialog) {
+ val typedValue = TypedValue()
+ resources.getValue(com.kunzisoft.androidclearchroma.R.dimen.chroma_dialog_height_multiplier, typedValue, true)
+ val heightMultiplier = typedValue.float
+ val height = (ad.context.resources.displayMetrics.heightPixels * heightMultiplier).toInt()
+
+ resources.getValue(com.kunzisoft.androidclearchroma.R.dimen.chroma_dialog_width_multiplier, typedValue, true)
+ val widthMultiplier = typedValue.float
+ val width = (ad.context.resources.displayMetrics.widthPixels * widthMultiplier).toInt()
+
+ ad.window?.setLayout(width, height)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ super.onCreateView(inflater, container, savedInstanceState)
+ return rootView
+ }
+
+ private inner class AfterColorSave(private val mNewColor: String,
+ private val mOldColor: String)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val defaultColorToShow =
+ if (result.isSuccess) {
+ mNewColor
+ } else {
+ database?.customColor = mOldColor
+ mOldColor
+ }
+ preference.summary = defaultColorToShow
+ }
+ }
+
+ companion object {
+ private const val TAG_FRAGMENT_COLORS = "TAG_FRAGMENT_COLORS"
+
+ @ColorInt
+ const val DEFAULT_COLOR: Int = Color.WHITE
+
+ fun newInstance(key: String): DatabaseColorPreferenceDialogFragmentCompat {
+ val fragment = DatabaseColorPreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ bundle.putInt(ARG_INITIAL_COLOR, Color.BLACK)
+ bundle.putInt(ARG_COLOR_MODE, ColorMode.RGB.ordinal)
+ bundle.putInt(ARG_INDICATOR_MODE, IndicatorMode.HEX.ordinal)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDataCompressionPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDataCompressionPreferenceDialogFragmentCompat.kt
index f68df11ee..fbee376de 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDataCompressionPreferenceDialogFragmentCompat.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDataCompressionPreferenceDialogFragmentCompat.kt
@@ -62,9 +62,7 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
if (compressionSelected != null) {
val newAlgorithm = compressionSelected
val oldAlgorithm = database.compressionAlgorithm
- newAlgorithm?.let {
- database.assignCompressionAlgorithm(it)
- }
+ database.compressionAlgorithm = newAlgorithm
if (oldAlgorithm != null && newAlgorithm != null)
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newAlgorithm, oldAlgorithm)
@@ -88,7 +86,7 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
if (result.isSuccess) {
mNewAlgorithm
} else {
- database?.assignCompressionAlgorithm(mOldAlgorithm)
+ database?.compressionAlgorithm = mOldAlgorithm
mOldAlgorithm
}
preference.summary = algorithmToShow.getName(settingsResources)
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDefaultUsernamePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDefaultUsernamePreferenceDialogFragmentCompat.kt
new file mode 100644
index 000000000..6c7cb52ff
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDefaultUsernamePreferenceDialogFragmentCompat.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePass DX.
+ *
+ * KeePass DX is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeePass DX is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeePass DX. If not, see .
+ *
+ */
+package com.kunzisoft.keepass.settings.preferencedialogfragment
+
+import android.os.Bundle
+import android.view.View
+import com.kunzisoft.keepass.tasks.ActionRunnable
+
+class DatabaseDefaultUsernamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
+
+ inputText = database?.defaultUsername?: ""
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ database?.let { database ->
+ if (positiveResult) {
+ val newDefaultUsername = inputText
+ val oldDefaultUsername = database.defaultUsername
+ database.defaultUsername = newDefaultUsername
+
+ actionInUIThreadAfterSaveDatabase = AfterDefaultUsernameSave(newDefaultUsername, oldDefaultUsername)
+ }
+ }
+
+ super.onDialogClosed(positiveResult)
+ }
+
+ private inner class AfterDefaultUsernameSave(private val mNewDefaultUsername: String,
+ private val mOldDefaultUsername: String)
+ : ActionRunnable() {
+
+ override fun onFinishRun(result: Result) {
+ val defaultUsernameToShow =
+ if (result.isSuccess) {
+ mNewDefaultUsername
+ } else {
+ database?.defaultUsername = mOldDefaultUsername
+ mOldDefaultUsername
+ }
+ preference.summary = defaultUsernameToShow
+ }
+ }
+
+ companion object {
+
+ fun newInstance(key: String): DatabaseDefaultUsernamePreferenceDialogFragmentCompat {
+ val fragment = DatabaseDefaultUsernamePreferenceDialogFragmentCompat()
+ val bundle = Bundle(1)
+ bundle.putString(ARG_KEY, key)
+ fragment.arguments = bundle
+
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.kt
index 54b4485cd..e08bdb851 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseDescriptionPreferenceDialogFragmentCompat.kt
@@ -32,12 +32,14 @@ class DatabaseDescriptionPreferenceDialogFragmentCompat : DatabaseSavePreference
}
override fun onDialogClosed(positiveResult: Boolean) {
- if (database != null && positiveResult) {
- val newDescription = inputText
- val oldDescription = database!!.description
- database?.assignDescription(newDescription)
+ database?.let { database ->
+ if (positiveResult) {
+ val newDescription = inputText
+ val oldDescription = database.description
+ database.description = newDescription
- actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newDescription, oldDescription)
+ actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newDescription, oldDescription)
+ }
}
super.onDialogClosed(positiveResult)
@@ -52,7 +54,7 @@ class DatabaseDescriptionPreferenceDialogFragmentCompat : DatabaseSavePreference
if (result.isSuccess) {
mNewDescription
} else {
- database?.assignDescription(mOldDescription)
+ database?.description = mOldDescription
mOldDescription
}
preference.summary = descriptionToShow
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.kt
index 825c84bd1..38aef811e 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.kt
@@ -59,13 +59,11 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
if (positiveResult) {
database?.let { database ->
- if (database.allowEncryptionAlgorithmModification()) {
+ if (database.allowEncryptionAlgorithmModification) {
if (algorithmSelected != null) {
val newAlgorithm = algorithmSelected
val oldAlgorithm = database.encryptionAlgorithm
- newAlgorithm?.let {
- database.assignEncryptionAlgorithm(it)
- }
+ database.encryptionAlgorithm = newAlgorithm
if (oldAlgorithm != null && newAlgorithm != null)
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newAlgorithm, oldAlgorithm)
@@ -90,7 +88,7 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
if (result.isSuccess) {
mNewAlgorithm
} else {
- database?.assignEncryptionAlgorithm(mOldAlgorithm)
+ database?.encryptionAlgorithm = mOldAlgorithm
mOldAlgorithm
}
preference.summary = algorithmToShow.getName(settingsResources)
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.kt
index b7ba2f7f5..6b05c14a7 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseKeyDerivationPreferenceDialogFragmentCompat.kt
@@ -62,11 +62,11 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
override fun onDialogClosed(positiveResult: Boolean) {
if (positiveResult) {
database?.let { database ->
- if (database.allowKdfModification()) {
+ if (database.allowKdfModification) {
val newKdfEngine = kdfEngineSelected
val oldKdfEngine = database.kdfEngine
if (newKdfEngine != null && oldKdfEngine != null) {
- database.assignKdfEngine(newKdfEngine)
+ database.kdfEngine = newKdfEngine
actionInUIThreadAfterSaveDatabase = AfterDescriptionSave(newKdfEngine, oldKdfEngine)
}
}
@@ -101,7 +101,7 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
if (result.isSuccess) {
mNewKdfEngine
} else {
- database?.assignKdfEngine(mOldKdfEngine)
+ database?.kdfEngine = mOldKdfEngine
mOldKdfEngine
}
preference.summary = kdfEngineToShow.getName(settingsResources)
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.kt
index a9a3603fe..8b4372037 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseNamePreferenceDialogFragmentCompat.kt
@@ -36,7 +36,7 @@ class DatabaseNamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogF
database?.let { database ->
val newName = inputText
val oldName = database.name
- database.assignName(newName)
+ database.name = newName
actionInUIThreadAfterSaveDatabase = AfterNameSave(newName, oldName)
}
@@ -54,7 +54,7 @@ class DatabaseNamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogF
if (result.isSuccess) {
mNewName
} else {
- database?.assignName(mOldName)
+ database?.name = mOldName
mOldName
}
preference.summary = nameToShow
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt
index 0f6fe8888..913b4d018 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DatabaseSavePreferenceDialogFragmentCompat.kt
@@ -20,6 +20,7 @@
package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.content.res.Resources
+import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
@@ -36,10 +37,14 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialo
protected lateinit var settingsResources: Resources
- override fun onBindDialogView(view: View) {
- super.onBindDialogView(view)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
this.database = Database.getInstance()
+ }
+
+ override fun onBindDialogView(view: View) {
+ super.onBindDialogView(view)
activity?.resources?.let { settingsResources = it }
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt
index d75e1ac1e..ad181fa51 100644
--- a/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ActionRunnable.kt
@@ -24,6 +24,7 @@ import android.content.Context
import android.os.Bundle
import android.util.Log
import android.widget.Toast
+import com.kunzisoft.keepass.database.exception.LoadDatabaseException
/**
* Callback after a task is completed.
@@ -52,8 +53,21 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
* launch the nested action runnable if exists and finish,
* else directly finish
*/
- protected fun finishRun(isSuccess: Boolean, message: String? = null) {
+ protected fun finishRun(isSuccess: Boolean,
+ message: String? = null) {
+ finishRun(isSuccess, null, message)
+ }
+
+ /**
+ * If [success] or [executeNestedActionIfResultFalse] true,
+ * launch the nested action runnable if exists and finish,
+ * else directly finish
+ */
+ protected fun finishRun(isSuccess: Boolean,
+ exception: LoadDatabaseException?,
+ message: String? = null) {
result.isSuccess = isSuccess
+ result.exception = exception
result.message = message
if (isSuccess || executeNestedActionIfResultFalse) {
execute()
@@ -89,5 +103,8 @@ abstract class ActionRunnable(private var nestedActionRunnable: ActionRunnable?
/**
* Class to manage result from ActionRunnable
*/
- data class Result(var isSuccess: Boolean = true, var message: String? = null, var data: Bundle? = null)
+ data class Result(var isSuccess: Boolean = true,
+ var message: String? = null,
+ var exception: LoadDatabaseException? = null,
+ var data: Bundle? = null)
}
diff --git a/app/src/main/res/layout/activity_group.xml b/app/src/main/res/layout/activity_group.xml
index 25d6d43d1..eeace1184 100644
--- a/app/src/main/res/layout/activity_group.xml
+++ b/app/src/main/res/layout/activity_group.xml
@@ -72,22 +72,39 @@
-
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/pref_dialog_input_numbers.xml b/app/src/main/res/layout/pref_dialog_input_numbers.xml
index 3a961e91d..5082fd234 100644
--- a/app/src/main/res/layout/pref_dialog_input_numbers.xml
+++ b/app/src/main/res/layout/pref_dialog_input_numbers.xml
@@ -38,7 +38,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/>
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index bce2000a5..bf891c822 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -27,7 +27,6 @@
لا تظهر مرة أخرى
أقواس
تمديد ASCII
- إلغاء
السماح
مُسِحت الحافظة
خطأ في الحافظة
@@ -54,7 +53,6 @@
اكتب عنوانًا.
اسم الحقل
قيمة الحقل
- تعذر إيجاد الملف.
توليد كلمة سر
تأكيد كلمة السر
اسم المجموعة
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index e4b3f44ee..98deec432 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -34,7 +34,6 @@
Paràmetres de l\'aplicació
Parèntesis
L\'exploració d\'arxius necessita l\'aplicació Open Intents File Manager, clica a sota per instal·lar-la. Degut a peculiaritats de l\'explorador d\'arxius pot ser que no funcioni correctament la primera execució.
- Cancel·la
Porta-retalls netejat.
Temps d\'espera del porta-retalls
Temps abans de netejar el porta-retalls després de copiar un usuari o contrasenya
@@ -72,7 +71,6 @@
Massa passades. Establint a 2147483648.
És necessari un títol.
Insereix un enter positiu al camp longitud
- Arxiu no trobat.
Explorador d\'arxius
Generar contrasenya
confirma contrasenya
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 3948869d2..f11a40238 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -34,7 +34,6 @@
Znovu neukázat
Závorky
Instalace správce souborů OpenIntents k procházení souborů
- Storno
Schránka vyčištěna
Chyba schránky
Některé Android telefony od Samsungu nedovolují aplikacím používat schránku.
@@ -79,7 +78,6 @@
Do nastavení „Délka“ zadejte celé kladné číslo.
Název pole
Hodnota pole
- Soubor nenalezen.
Správce souborů
Vytvoř heslo
potvrď heslo
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 0b167661f..ac9d55474 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -34,7 +34,6 @@
Vis ikke igen
Parenteser
Installer OpenIntents Fil Manager for at gennemse filer
- Annuller
Udklipsholder ryddet
Udklipsfejl
Nogle Samsung Android-telefoner, vil ikke lade programmer bruge udklipsholderen.
@@ -78,7 +77,6 @@
Angiv et positivt heltal i feltet \"Længde\".
Feltnavn
Feltværdi
- Kunne ikke finde filen.
Filhåndtering
Generer adgangskode
bekræft adgangskode
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 3ab4f6a81..e459e4356 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -36,7 +36,6 @@
Nicht mehr anzeigen
Klammern
Durchsuchen Sie Ihre Dateien, indem Sie den OpenIntents File Manager installieren
- Abbrechen
Zwischenablage geleert
Zwischenablagefehler
Einige Samsung Android-Smartphones lassen keine Nutzung der Zwischenablage durch Apps zu.
@@ -81,7 +80,6 @@
Eine positive ganze Zahl in das Feld „Länge“ eingeben.
Feldname
Feldwert
- Datei nicht gefunden.
Datei nicht gefunden. Bitte versuchen, sie über den Dateimanager zu öffnen.
Dateimanager
Passwort generieren
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index bafaee594..eaed388d2 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -32,7 +32,6 @@
Να μην ερωτηθώ ξανά
Αγκύλες
Η αναζήτηση αρχείων απαιτεί τον Διαχειριστή Αρχείων Open Intents, πατήστε παρακάτω για να τον εγκαταστήσετε. Λόγω μερικών ιδιορρυθμιών στον διαχειριστή αρχείων, η περιήγηση μπορεί να μην λειτουργεί σωστά την πρώτη φορά που θα περιηγηθείτε.
- Ακύρωση
Το πρόχειρο καθαρίστηκε.
Σφάλμα προχείρου
Μερικά Android κινητά τηλέφωνα της Samsung έχουν ένα σφάλμα στην εφαρμογή του προχείρου που προκαλεί την αντιγραφή από εφαρμογές να αποτυγχάνει. Για περισσότερες πληροφορίες πηγαίνετε:
@@ -77,7 +76,6 @@
Εισάγετε έναν θετικό ακέραιο αριθμό στο πεδίο μήκους
Όνομα Πεδίου
Τιμή πεδίου
- Το αρχείο δεν βρέθηκε.
Διαχείριση Αρχείων
Δημιουργία Κωδικού
επιβεβαίωση κωδικού
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index b3d025331..d3a94100c 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -33,7 +33,6 @@
Configuración de la aplicación
Paréntesis
Explora ficheros con OpenIntents File Manager
- Cancelar
Portapapeles limpiado
Portapapeles caducado
Duración de almacemiento en el portapapeles
@@ -71,7 +70,6 @@
Pasadas demasiado grande. Establecido a 2147483648.
Se necesita un título.
Introduzca un entero positivo en el campo longitud
- Archivo no encontrado.
Explorador de Archivos
Generar Contraseña
confirmar contraseña
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 27f553382..7a968d736 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -33,7 +33,6 @@
Ez erakutsi berriro
Brackets
Fitxategietan nabigatzeak Open Intents Fitxategi Kudeatzailea behar du. Klik egin azpian instalatzeko. Fitxategien kudeatzailaren arazo batzuk direla eta, izan daiteke nabigazioak ondo ez funtzionatzea lehenengo aldian.
- Utzi
Arbela ezabatuta.
Arbelean errorea
Samsung Android telefono batzuek akats bat daukate arbelaren inplementazioan, eta honen ondorioz aplikazioetatik kopiatzeak huts egiten du. Xehetasun gehiagotarako ondokora joan:
@@ -78,7 +77,6 @@
Eremuaren luzeran entero positibo bat sartu
Eremuaren izena
Eremuaren balorea
- Fitxategi ez aurkitua.
Fitxategien nabigatzailea
Pasahitza sortu
pasahitza berretsi
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 3b2bb8be2..ec3196f1b 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -32,7 +32,6 @@
Älä näytä enää uudelleen
Hakasulkeet
Tiedostojen selaus vaatii Open Intents File Manager -tiedostonhallintaohjelman, klikkaa alla olevaa linkkiä asentaaksesi sen. Joidenkin ominaisuuksien takia se ei ehkä toimi oikein ensimmäisellä käynnistyksellä.
- Peruuta
Leikepöytä tyhjennetty.
Leikepöytävirhe
Joissakin Android-puhelimissa on virhe leikepöydän toteutuksessa, mikä aiheuttaa kopioinnin epäonnistumisen. Lisätietoa:
@@ -77,7 +76,6 @@
Syötä positiivinen kokonaisluku pituus-kenttään
Kentän nimi
Kentän arvo
- Tiedostoa ei löydetty.
Tiedostoselain
Generoi salasana
vahvista salasana
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 718d903f9..934953fe7 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -36,7 +36,6 @@
Crochets
ASCII étendu
Parcourir les fichiers en installant le gestionnaire de fichiers OpenIntents
- Annuler
Autoriser
Presse-papier vidé
Erreur de presse-papier
@@ -83,7 +82,6 @@
Impossible d’activer le service de remplissage automatique.
Nom du champ
Valeur du champ
- Impossible de trouver le fichier.
Impossible de trouver le fichier. Essayer de le rouvrir depuis votre gestionnaire de fichiers.
Gestionnaire de fichiers
Générer un mot de passe
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 03d105380..3d1a2f4f9 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -14,7 +14,6 @@
Non amosar de novo
Parénteses
ASCII extendido
- Cancelar
Permitir
Portapapeis limpo
Erro do portapapeis
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 048b63ef6..3ebaf1683 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -32,7 +32,6 @@
"Ne mutassa többet"
Zárójelek
Fájlok böngészése az OpenIntents fájlkezelő telepítésével
- Mégse
Vágólap törölve
Vágólap hiba
Egyes androidos Samsung telefonok nem engedik, hogy az alkalmazások használják a vágólapot.
@@ -76,7 +75,6 @@
Írjon be egy pozitív egész számot a „Hossz” mezőbe.
Mezőnév
Mezőérték
- A fájl nem található.
A fájl nem található. Próbálja meg újra megnyitni a fájlkezelőben.
Fájlkezelő
Jelszó előállítása
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 4d5a73bd6..3edc21733 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -33,7 +33,6 @@
Impostazioni app
Parentesi
Sfoglia i file installando il Gestore File di OpenIntents
- Annulla
Appunti eliminati
Errore negli appunti
Alcuni telefoni Android di Samsung non permettono alle app di usare gli appunti.
@@ -77,7 +76,6 @@
Inserisci un numero naturale positivo nel campo \"lunghezza\".
Nome campo
Valore campo
- File non trovato.
File non trovato. Prova a riaprirlo dal tuo gestore di file.
Gestore file
Genera password
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 13446386d..cf95d67a9 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -31,7 +31,6 @@
אל תציג שוב
סוגריים
סייר הקבצים דורש את סייר הקבצים Open Intents, לחץ למטע כדי להתקין. בגלל מספר בעיות בסייר, ייתכן ויהיו בעיות בהפעלה הראשונה.
- בטל
לוח ההעתקה נוקה.
שגיאת לוח ההעתקה
במספר מכשירי אנרואיד מסמסונג קיים באג במימוש לוח ההעתקה שיכול לגרום לבעיות בהעתקה מהיישום. לעוד מידע עבור אל:
@@ -74,7 +73,6 @@
הזן מספר חיובי בשדה האורך
שם השדה
ערך השדה
- קובץ לא נמצא.
סייר קבצים
צור סיסמה
אשר סיסמה
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 616ecaa51..c4298a2c2 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -30,7 +30,6 @@
アプリケーション設定
カッコ
ファイルを検索するには OI File Manager が必要です。
- キャンセル
クリップボードを消去しました。
クリップボード タイムアウト
コピーした情報をクリップボードから消去する時間
@@ -68,7 +67,6 @@
値が大きすぎます。 2147483648にセットしました。
タイトルは必須入力です。
\"長さ\"欄には正の整数を入力してください。
- ファイルが見つかりません。
ファイルブラウザ
パスワードを生成する
パスワードをもう一度入力
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 7f476de45..5d30cb9e8 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -35,7 +35,6 @@
브라켓
확장 ASCII
OpenIntents File Manager를 설치하여 파일 찾아보기
- 취소
허가
클립보드 비워짐
클립보드 오류
@@ -85,7 +84,6 @@
그룹을 자신에게 옮길 수 없습니다.
필드 이름
필드 값
- 파일을 찾을 수 없습니다.
파일을 찾을 수 없습니다. 파일 탐색기에서 열리는지 확인해 주세요.
파일 탐색기
비밀번호 생성
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 6a733d516..ff9a7e1f2 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -2,7 +2,6 @@
KeePass DX yra KeePass slaptažodžių tvarkyklės realizacija Android platformai
Iškarpinė išvalyta.
- Failas nerastas.
Neteisingas slaptažodis arba rakto failas.
Atsiliepimai:
Pagrindinis puslapis:
@@ -15,7 +14,6 @@
Programėlės nustatymai
Daugiau neberodyti
Skliaustai
- Atšaukti
Duomenų bazė
Skaitmenys
Atšaukti
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index 579e34c32..b3b6be234 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -14,7 +14,6 @@
Turpmāk nerādīt
Iekavas
Failu pārlūkošanai nepieciešams pārlūks.
- Atcelt
Starpliktuve notīrīta
Starpliktuves kļūda
Dažiem Samsung tālruņiem ir problēmas ar starpliktuves lietošanu. Lai saņemtu sīkāku informāciju, dodieties uz:
@@ -57,7 +56,6 @@
Norādiet garumu lielāku par nulli
Lauka nosaukums
Lauka vērtība
- Fails nav atrasts.
Failu pārlūks
Ģenerēt Paroli
apstipriniet paroli
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index 29c250cd1..f913060f1 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -35,7 +35,6 @@
Parenteser
Utvidet ASCII
Utforsk filer ved å installere OpenIntents-filbehandleren
- Avbryt
Tillat
Utklippstavle tømt
Utklippstavlefeil
@@ -85,7 +84,6 @@
Kan ikke flytte gruppe inn i seg selv.
Feltnavn
Feltverdi
- Fant ikke filen.
Fant ikke filen. Prøv å åpne den fra din innholdsleverandør.
Filutforsker
Opprett passord
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index f44cc2ff6..f1607444c 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -33,7 +33,6 @@
App-instellingen
Haakjes
Zoek een bestand op door het installeren van de OpenIntents File Manager
- Annuleren
Klembord gewist
Klembordtime-out
Tijd van opslag op het klembord
@@ -71,7 +70,6 @@
\"Cycli-waarde\" te groot. Wordt ingesteld op 2147483648.
Voeg een titel toe.
Voer een positief geheel getal in in het veld \"Lengte\".
- Bestand niet gevonden.
Bestandsverkenner
Wachtwoord genereren
wachtwoord bevestigen
diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml
index 0d480cd4f..3ea0729eb 100644
--- a/app/src/main/res/values-nn/strings.xml
+++ b/app/src/main/res/values-nn/strings.xml
@@ -31,7 +31,6 @@
Programinnstillingar
Parentesar
Du må ha Open Intents filbehandlar for å kunna bla i filer. Klikk nedanfor for å installera han. Grunna nokre småfeil i programmet kan det vera at det ikkje fungerer heilt den første gongen du bruker det.
- Avbryt
Utklippstavla er tømt.
Tidsavbrot på utklippstavla
Tid før utklippstavla blir tømt etter at brukarnamnet eller passordet er kopiert.
@@ -69,7 +68,6 @@
For mange omgangar. Bruker 2147483648.
Treng ein tittel.
Bruk eit positivt heiltal i lengdfeltet
- Fann ikkje fila.
Filbehandlar
Lag passord
stadfest passordet
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 7af14e69e..78c8b9304 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -31,7 +31,6 @@
Ustawienia aplikacji
Nawiasy
Przeglądaj pliki, instalując Menedżera plików OpenIntents
- Anuluj
Schowek został wyczyszczony
Czas wygaśnięcia schowka
Czas przechowywania w schowku
@@ -68,7 +67,6 @@
\"Rundy szyfrowania\" są zbyt wysokie. Ustaw na 2147483648.
Dodaj tytuł.
Wprowadź dodatnią liczbę całkowitą w polu \"Długość\".
- Nie znaleziono pliku.
Przeglądarka plików
Generuj hasło
potwierdź hasło
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index e0e309ddc..1caf84b35 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -33,7 +33,6 @@
Configurações do aplicativo
Parênteses
Procure arquivos instalando o OpenIntents File Manager
- Cancelar
Área de transferência limpa
Tempo limite para o clipboard
Duração do armazenamento na área de transferência
@@ -71,7 +70,6 @@
\"Número de rodadas\" é muito grande. Modificado para 2147483648.
Insira um título.
Digite um número inteiro positivo no campo \"Tamanho\".
- Não pôde encontrar o arquivo.
Localizador de arquivos
Gerar senha
confirmar senha
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 4337182db..bbfba6a7c 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -32,7 +32,6 @@
Não mostrar novamente
Parênteses
Explore ficheiros instalando o gestor de ficheiros OpenIntents
- Cancelar
Área de transferência limpa
Erro na área de transferência
Alguns dispositivos Samsung Android não deixam as apps usarem a área de transferência.
@@ -77,7 +76,6 @@
Digite um número inteiro positivo no campo \"Tamanho\".
Nome do campo
Valor do campo
- Não pôde encontrar o ficheiro.
Arquivo não encontrado. Tente reabrí-lo de seu provedor de conteúdo.
Localizador de ficheiros
Gerar palavra-chave
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 9d437326a..b7ce3614b 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -32,7 +32,6 @@
Не показывать снова
{[(Скобки)]}
Для обзора файлов установите OpenIntents File Manager
- Отмена
Буфер обмена очищен
Ошибка буфера обмена
Некоторые устройства Samsung не дают приложению использовать буфер обмена.
@@ -77,7 +76,6 @@
Поле \"Длина\" должно быть положительным целым числом.
Название поля
Значение поля
- Файл не найден.
Файл не найден. Попробуйте повторно открыть через встроенный поставщик содержимого.
Обзор файлов
Генерация пароля
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 7fcf15cc4..1d3ec2b72 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -30,7 +30,6 @@
Nastavenia aplikácie
Konzoly
Prezeranie súborov vyžaduje otvorenie Správcu súborov, kliknite nižšie pre inštalovanie. Kôli chybám v správcovi súborov, prehľadávanie nemusí pracovať správne, ak prehľadávate prvý krát.
- Zrušiť
Schránka vyčistená.
Timeout Schránky
Čas uchovania v schránke
@@ -68,7 +67,6 @@
Príliš veľa opakovaní. Nastavujem na 2147483648.
Vyžaduje sa názov.
Zadajte celé kladné číslo na dĺžku poľa
- Súbor nenájdený.
Správca Súborov
Generovať Heslo
potvrdiť heslo
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index dae3dab6e..269f46802 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -33,7 +33,6 @@
Visa inte igen
Parenteser
Filhantering kräver Open Intents File Manager, klicka nedan för att installera. Filhanteraren kanske inte fungerar korrekt vid första användningen.
- Avbryt
Urklippet är rensat.
Urklippsfel
Vissa Samsung-telefoner har en bugg som gör att applikationer inte kan kopiera till urklipp. För mer detaljer, gå till:
@@ -77,7 +76,6 @@
Ange ett positivt heltal i fältet för längd
Fältnamn
Fältvärde
- Filen hittades inte.
Filhanterare
Generera lösenord
bekräfta lösenord
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 832942798..e3284f354 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -35,7 +35,6 @@
Parantez
Genişletilmiş ASCII
OpenIntents Dosya Yöneticisi\'ni yükleyerek dosyalara göz atın
- İptal
İzin ver
Pano temizlendi
Pano hatası
@@ -85,7 +84,6 @@
Bir grubu kendine taşıyamazsın.
Alan adı
Alan değeri
- Dosya bulunamadı.
Dosya bulunamadı. Dosya tarayıcınızda yeniden açmayı deneyin.
Dosya tarayıcı
Parola üret
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 8cb761c40..e6a70ad6b 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -31,7 +31,6 @@
Налаштування програми
Дужки
Для перегляду файла необхідно Open Intents File Manager, натисніть нижче для його інсталяції. У зв’язку з деякими недоробками у менеджері файлів перегляд може працювати некоректно при запуску перший раз.
- Відміна
Буфер обміну очищено.
Тайм-аут буфера обміну
Час через який буде очищено буфер обміну після копіювання ім’я користувача чи пароля
@@ -69,7 +68,6 @@
Надто багато циклів. Установлено 2147483648.
Необхідно вказати заголовок.
Введіть ціле число на усю довжину поля
- Файл не знайдено.
Перегляд файлів
Згенерувати пароль
підтвердження пароля
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 11c50efec..fcd56c444 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -31,7 +31,6 @@
应用设置
括号
安装 OpenIntents File Manager 应用来选取文件
- 取消
剪贴板已清空
剪贴板清空延时
剪贴板保存时间
@@ -69,7 +68,6 @@
“变换次数”过多。已设置为 2147483648。
请添加标题。
请在“长度”字段输入一个正整数。
- 找不到文件。
文件浏览器
生成密码
确认密码
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 7c17ea228..5cfc8d24d 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -30,7 +30,6 @@
應用程式設定
括弧
流覽檔需要安裝Open Intents File Manager軟體,點下面安裝。由於一些檔管理軟體的差異,在你第一次瀏覽時可能無法正常工作。
- 取消
剪貼板已清除
剪貼板超時
複製用戶名或密碼到剪貼板後清除的時間
@@ -68,7 +67,6 @@
次數太多。最大設置到2147483648。
標題為必填。
長度欄位輸入一個正整數
- 文件未找到。
檔案管理器
生成密碼
確認密碼
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 28e6fea44..1bcc0cd0e 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -148,16 +148,20 @@
settings_database_security_key
settings_database_credentials_key
- database_general_key
+ database_category_general_key
database_name_key
database_description_key
database_default_username_key
database_custom_color_key
database_version_key
- database_history_key
+ database_category_compression_key
database_data_compression_key
+
+ database_category_recycle_bin_key
recycle_bin_key
+
+ database_category_history_key
max_history_items_key
max_history_size_key
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 767e34007..bdcead124 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -37,7 +37,6 @@
Brackets
Extended ASCII
Create, Open and Save a database file requires installing a file manager that accepts the Intent action ACTION_CREATE_DOCUMENT and ACTION_OPEN_DOCUMENT
- Cancel
Allow
Clipboard cleared
Clipboard error
@@ -101,6 +100,7 @@
Could not load your database.
Could not load the key. Try to lower the KDF \"Memory Usage\".
At least one password generation type must be selected.
+ At lest one credential must be set.
The passwords do not match.
\"Transformation rounds\" too high. Setting to 2147483648.
Each string must have a field name.
@@ -113,7 +113,6 @@
Unable to create database with this password and key file.
Field name
Field value
- Could not find file.
Could not find file. Try reopening it from your file browser.
File browser
Generate password
@@ -128,6 +127,7 @@
Install from Play Store
Could not read password or keyfile.
Wrong algorithm.
+ %1$s with the same UUID %2$s already exists.
Could not recognize the database format.
No keyfile exists.
The keyfile is empty.
@@ -183,6 +183,8 @@
Write-protected
KeePass DX needs write permission in order to change anything in your database.
Starting with Android KitKat, some devices no longer allow apps to write to the SD card.
+ The database contains duplicate UUIDs.
+ By validating this dialog, KeePass DX will fix the problem (by generating new UUIDs for duplicates) and continue.
Selection mode
Recent file history
Remember recent filenames
diff --git a/app/src/main/res/xml/preferences_database.xml b/app/src/main/res/xml/preferences_database.xml
index 4eadcce9b..42772b401 100644
--- a/app/src/main/res/xml/preferences_database.xml
+++ b/app/src/main/res/xml/preferences_database.xml
@@ -21,7 +21,7 @@
xmlns:android="http://schemas.android.com/apk/res/android">
-
-
-
+ android:title="@string/database_custom_color_title"
+ android:summary="[color]"
+ chroma:chromaShapePreview="ROUNDED_SQUARE"
+ chroma:chromaColorMode="RGB"
+ chroma:chromaIndicatorMode="HEX" />