diff --git a/CHANGELOG b/CHANGELOG
index 0fdaf8a2d..973ad31d2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,13 @@
+KeePassDX(3.5.0)
+ * Support YubiKey challenge-response #8 #137
+ * Better exception management during database save #1346
+ * Add "Screenshot mode" setting #459 #1377 #1354 (Thx @GianpaMX)
+ * Hide clipboard sensitive text when copy entry field #1386
+ * Fix attachment download button #1401
+ * Add monochrome icon #1403 #1404 (Thx @Sandelinos)
+ * Fix lock with back button #1412 #1414 (Thx @ryg-git)
+ * Vanadium compatibility #1447 (Thx @flawedworld)
+
KeePassDX(3.4.5)
* Fix custom data in group (fix KeeShare) #1335
* Fix device credential unlocking #1344
diff --git a/Gemfile.lock b/Gemfile.lock
index f576a4052..bc807e7a9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -3,25 +3,25 @@ GEM
specs:
CFPropertyList (3.0.5)
rexml
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
- aws-partitions (1.577.0)
- aws-sdk-core (3.130.1)
+ aws-partitions (1.646.0)
+ aws-sdk-core (3.160.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
- jmespath (~> 1.0)
- aws-sdk-kms (1.55.0)
+ jmespath (~> 1, >= 1.6.1)
+ aws-sdk-kms (1.58.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.113.0)
+ aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
- aws-sigv4 (1.4.0)
+ aws-sigv4 (1.5.2)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
@@ -34,10 +34,10 @@ GEM
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
- dotenv (2.7.6)
+ dotenv (2.8.1)
emoji_regex (3.2.3)
- excon (0.92.2)
- faraday (1.10.0)
+ excon (0.93.0)
+ faraday (1.10.2)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
@@ -56,8 +56,8 @@ GEM
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
- faraday-multipart (1.0.3)
- multipart-post (>= 1.2, < 3)
+ faraday-multipart (1.0.4)
+ multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
@@ -66,7 +66,7 @@ GEM
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
- fastlane (2.205.1)
+ fastlane (2.210.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -107,9 +107,9 @@ GEM
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-versioning_android (0.1.0)
gh_inspector (1.1.3)
- google-apis-androidpublisher_v3 (0.19.0)
- google-apis-core (>= 0.4, < 2.a)
- google-apis-core (0.4.2)
+ google-apis-androidpublisher_v3 (0.29.0)
+ google-apis-core (>= 0.9.0, < 2.a)
+ google-apis-core (0.9.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
@@ -118,27 +118,27 @@ GEM
retriable (>= 2.0, < 4.a)
rexml
webrick
- google-apis-iamcredentials_v1 (0.10.0)
- google-apis-core (>= 0.4, < 2.a)
- google-apis-playcustomapp_v1 (0.7.0)
- google-apis-core (>= 0.4, < 2.a)
- google-apis-storage_v1 (0.13.0)
- google-apis-core (>= 0.4, < 2.a)
+ google-apis-iamcredentials_v1 (0.15.0)
+ google-apis-core (>= 0.9.0, < 2.a)
+ google-apis-playcustomapp_v1 (0.11.0)
+ google-apis-core (>= 0.9.0, < 2.a)
+ google-apis-storage_v1 (0.19.0)
+ google-apis-core (>= 0.9.0, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
- google-cloud-errors (1.2.0)
- google-cloud-storage (1.36.1)
+ google-cloud-errors (1.3.0)
+ google-cloud-storage (1.43.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
- google-apis-storage_v1 (~> 0.1)
+ google-apis-storage_v1 (~> 0.19.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
- googleauth (1.1.2)
+ googleauth (1.2.0)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
@@ -146,12 +146,12 @@ GEM
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
- http-cookie (1.0.4)
+ http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.1)
- json (2.6.1)
- jwt (2.3.0)
+ json (2.6.2)
+ jwt (2.5.0)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.2)
@@ -162,9 +162,9 @@ GEM
optparse (0.1.1)
os (1.1.4)
plist (3.6.0)
- public_suffix (4.0.7)
+ public_suffix (5.0.0)
rake (13.0.6)
- representable (3.1.1)
+ representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
@@ -174,9 +174,9 @@ GEM
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
- signet (0.16.1)
+ signet (0.17.0)
addressable (~> 2.8)
- faraday (>= 0.17.5, < 3.0)
+ faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
@@ -193,11 +193,11 @@ GEM
uber (0.1.0)
unf (0.1.4)
unf_ext
- unf_ext (0.0.8.1)
+ unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
word_wrap (1.0.0)
- xcodeproj (1.21.0)
+ xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
diff --git a/README.md b/README.md
index 8b9d32ce1..b1d70da7b 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
- Create database files / entries and groups.
- Support for **.kdb** and **.kdbx** files (version 1 to 4) with AES - Twofish - ChaCha20 - Argon2 algorithm.
- - **Compatible** with the majority of alternative programs (KeePass, KeePassX, KeePassXC, …).
+ - **Compatible** with the majority of alternative programs (KeePass, KeePassXC, KeeWeb, …).
- Allows opening and **copying URI / URL fields quickly**.
- **Biometric recognition** for fast unlocking *(fingerprint / face unlock / …)*.
- **One-Time Password** management *(HOTP / TOTP)* for Two-factor authentication (2FA).
@@ -74,7 +74,7 @@ Other questions? You can read the [FAQ](https://github.com/Kunzisoft/KeePassDX/w
## License
- Copyright © 2022 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com).
+ Copyright © 2023 Jeremy Jamet / [Kunzisoft](https://www.kunzisoft.com).
This file is part of KeePassDX.
diff --git a/app/build.gradle b/app/build.gradle
index cd5f7ea0b..c91996b74 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,8 +12,8 @@ android {
applicationId "com.kunzisoft.keepass"
minSdkVersion 15
targetSdkVersion 32
- versionCode = 114
- versionName = "3.4.5"
+ versionCode = 118
+ versionName = "3.5.0"
multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests"
@@ -93,7 +93,7 @@ android {
}
}
-def room_version = "2.4.2"
+def room_version = "2.4.3"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
@@ -101,14 +101,14 @@ dependencies {
implementation "androidx.appcompat:appcompat:$android_appcompat_version"
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.cardview:cardview:1.0.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'androidx.media:media:1.6.0'
// Lifecycle - LiveData - ViewModel - Coroutines
implementation "androidx.core:core-ktx:$android_core_version"
- implementation 'androidx.fragment:fragment-ktx:1.4.1'
+ implementation 'androidx.fragment:fragment-ktx:1.5.2'
implementation "com.google.android.material:material:$android_material_version"
// Token auto complete
// From sources until https://github.com/splitwise/TokenAutoComplete/pull/422 fixed
diff --git a/app/schemas/com.kunzisoft.keepass.app.database.AppDatabase/2.json b/app/schemas/com.kunzisoft.keepass.app.database.AppDatabase/2.json
new file mode 100644
index 000000000..a6e3f6fac
--- /dev/null
+++ b/app/schemas/com.kunzisoft.keepass.app.database.AppDatabase/2.json
@@ -0,0 +1,90 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "f8fb4aed546de19ae7ca0797f49b26a4",
+ "entities": [
+ {
+ "tableName": "file_database_history",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `database_alias` TEXT NOT NULL, `keyfile_uri` TEXT, `hardware_key` TEXT, `updated` INTEGER NOT NULL, PRIMARY KEY(`database_uri`))",
+ "fields": [
+ {
+ "fieldPath": "databaseUri",
+ "columnName": "database_uri",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "databaseAlias",
+ "columnName": "database_alias",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "keyFileUri",
+ "columnName": "keyfile_uri",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hardwareKey",
+ "columnName": "hardware_key",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "updated",
+ "columnName": "updated",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "database_uri"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "cipher_database",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `encrypted_value` TEXT NOT NULL, `specs_parameters` TEXT NOT NULL, PRIMARY KEY(`database_uri`))",
+ "fields": [
+ {
+ "fieldPath": "databaseUri",
+ "columnName": "database_uri",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "encryptedValue",
+ "columnName": "encrypted_value",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "specParameters",
+ "columnName": "specs_parameters",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "database_uri"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f8fb4aed546de19ae7ca0797f49b26a4')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/free/res/drawable-v24/ic_launcher_monochrome.xml b/app/src/free/res/drawable-v24/ic_launcher_monochrome.xml
new file mode 100644
index 000000000..9c82835ee
--- /dev/null
+++ b/app/src/free/res/drawable-v24/ic_launcher_monochrome.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/libre/res/drawable-v24/ic_launcher_monochrome.xml b/app/src/libre/res/drawable-v24/ic_launcher_monochrome.xml
new file mode 100644
index 000000000..c0491a088
--- /dev/null
+++ b/app/src/libre/res/drawable-v24/ic_launcher_monochrome.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index df3349851..f7ab6482b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -156,6 +156,9 @@
android:name="com.kunzisoft.keepass.settings.SettingsAdvancedUnlockActivity" />
+
(R.id.activity_about_privacy_text).apply {
+ movementMethod = LinkMovementMethod.getInstance()
+ text = HtmlCompat.fromHtml(getString(R.string.html_about_privacy),
+ HtmlCompat.FROM_HTML_MODE_LEGACY)
+ }
+
findViewById(R.id.activity_about_contribution_text).apply {
movementMethod = LinkMovementMethod.getInstance()
text = HtmlCompat.fromHtml(getString(R.string.html_about_contribution),
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 9ad584793..e10721e04 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
@@ -55,7 +55,8 @@ import com.kunzisoft.keepass.autofill.AutofillComponent
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
@@ -66,6 +67,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.*
import com.kunzisoft.keepass.view.asError
+import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import com.kunzisoft.keepass.viewmodels.DatabaseFilesViewModel
import java.io.FileNotFoundException
@@ -155,8 +157,9 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen ->
fileDatabaseHistoryEntityToOpen.databaseUri?.let { databaseFileUri ->
launchPasswordActivity(
- databaseFileUri,
- fileDatabaseHistoryEntityToOpen.keyFileUri
+ databaseFileUri,
+ fileDatabaseHistoryEntityToOpen.keyFileUri,
+ fileDatabaseHistoryEntityToOpen.hardwareKey
)
}
}
@@ -250,7 +253,8 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
?: MainCredential()
databaseFilesViewModel.addDatabaseFile(
databaseUri,
- mainCredential.keyFileUri
+ mainCredential.keyFileUri,
+ mainCredential.hardwareKey
)
}
}
@@ -268,18 +272,8 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
launchGroupActivityIfLoaded(database)
}
}
- } else {
- var resultError = ""
- val resultMessage = result.message
- // Show error message
- if (resultMessage != null && resultMessage.isNotEmpty()) {
- resultError = "$resultError $resultMessage"
- }
- Log.e(TAG, resultError)
- Snackbar.make(coordinatorLayout,
- resultError,
- Snackbar.LENGTH_LONG).asError().show()
}
+ coordinatorLayout.showActionErrorIfNeeded(result)
}
/**
@@ -297,10 +291,11 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
Snackbar.make(coordinatorLayout, error, Snackbar.LENGTH_LONG).asError().show()
}
- private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?) {
+ private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?, hardwareKey: HardwareKey?) {
MainCredentialActivity.launch(this,
databaseUri,
keyFile,
+ hardwareKey,
{ exception ->
fileNoFoundAction(exception)
},
@@ -321,7 +316,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
}
private fun launchPasswordActivityWithPath(databaseUri: Uri) {
- launchPasswordActivity(databaseUri, null)
+ launchPasswordActivity(databaseUri, null, null)
// Delete flickering for kitkat <=
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
overridePendingTransition(0, 0)
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 ee746d966..b658afdcd 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt
@@ -69,7 +69,7 @@ import com.kunzisoft.keepass.database.search.SearchParameters
import com.kunzisoft.keepass.education.GroupActivityEducation
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
import com.kunzisoft.keepass.model.GroupInfo
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
@@ -1368,7 +1368,6 @@ class GroupActivity : DatabaseLockActivity(),
EntrySelectionHelper.removeInfoFromIntent(intent)
if (PreferencesUtil.isLockDatabaseWhenBackButtonOnRootClicked(this)) {
lockAndExit()
- super.onRegularBackPressed()
} else {
backToTheAppCaller()
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt
index fad63269c..371a321ea 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt
@@ -56,9 +56,11 @@ import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.biometric.AdvancedUnlockFragment
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
import com.kunzisoft.keepass.education.PasswordActivityEducation
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.*
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.CIPHER_DATABASE_KEY
@@ -73,6 +75,7 @@ import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.view.MainCredentialView
import com.kunzisoft.keepass.view.asError
+import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import com.kunzisoft.keepass.viewmodels.AdvancedUnlockViewModel
import com.kunzisoft.keepass.viewmodels.DatabaseFileViewModel
import java.io.FileNotFoundException
@@ -101,6 +104,8 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private var mRememberKeyFile: Boolean = false
private var mExternalFileHelper: ExternalFileHelper? = null
+ private var mRememberHardwareKey: Boolean = false
+
private var mReadOnly: Boolean = false
private var mForceReadOnly: Boolean = false
@@ -133,11 +138,13 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
PreferencesUtil.enableReadOnlyDatabase(this)
}
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this)
+ mRememberHardwareKey = PreferencesUtil.rememberHardwareKey(this)
- mExternalFileHelper = ExternalFileHelper(this@MainCredentialActivity)
+ // Build elements to manage keyfile selection
+ mExternalFileHelper = ExternalFileHelper(this)
mExternalFileHelper?.buildOpenDocument { uri ->
if (uri != null) {
- mainCredentialView?.populateKeyFileTextView(uri)
+ mainCredentialView?.populateKeyFileView(uri)
}
}
mainCredentialView?.setOpenKeyfileClickListener(mExternalFileHelper)
@@ -171,6 +178,16 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
mAdvancedUnlockViewModel.checkUnlockAvailability()
enableConfirmationButton()
}
+ mainCredentialView?.onKeyFileChecked =
+ CompoundButton.OnCheckedChangeListener { _, _ ->
+ // TODO mAdvancedUnlockViewModel.checkUnlockAvailability()
+ enableConfirmationButton()
+ }
+ mainCredentialView?.onHardwareKeyChecked =
+ CompoundButton.OnCheckedChangeListener { _, _ ->
+ // TODO mAdvancedUnlockViewModel.checkUnlockAvailability()
+ enableConfirmationButton()
+ }
// Observe if default database
mDatabaseFileViewModel.isDefaultDatabase.observe(this) { isDefaultDatabase ->
@@ -204,10 +221,19 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
databaseKeyFileUri
}
+ val databaseHardwareKey = mainCredentialView?.getMainCredential()?.hardwareKey
+ val hardwareKey =
+ if (mRememberHardwareKey
+ && databaseHardwareKey == null) {
+ databaseFile?.hardwareKey
+ } else {
+ databaseHardwareKey
+ }
+
// Define title
filenameView?.text = databaseFile?.databaseAlias ?: ""
- onDatabaseFileLoaded(databaseFile?.databaseUri, keyFileUri)
+ onDatabaseFileLoaded(databaseFile?.databaseUri, keyFileUri, hardwareKey)
}
}
@@ -215,6 +241,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
super.onResume()
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this@MainCredentialActivity)
+ mRememberHardwareKey = PreferencesUtil.rememberHardwareKey(this@MainCredentialActivity)
// Back to previous keyboard is setting activated
if (PreferencesUtil.isKeyboardPreviousDatabaseCredentialsEnable(this@MainCredentialActivity)) {
@@ -266,90 +293,84 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
launchGroupActivityIfLoaded(database)
} else {
mainCredentialView?.requestPasswordFocus()
+ // Manage special exceptions
+ when (result.exception) {
+ is DuplicateUuidDatabaseException -> {
+ // Relaunch loading if we need to fix UUID
+ showLoadDatabaseDuplicateUuidMessage {
+
+ var databaseUri: Uri? = null
+ var mainCredential = MainCredential()
+ var readOnly = true
+ var cipherEncryptDatabase: CipherEncryptDatabase? = null
+
+ result.data?.let { resultData ->
+ databaseUri = resultData.getParcelable(DATABASE_URI_KEY)
+ mainCredential =
+ resultData.getParcelable(MAIN_CREDENTIAL_KEY)
+ ?: mainCredential
+ readOnly = resultData.getBoolean(READ_ONLY_KEY)
+ cipherEncryptDatabase =
+ resultData.getParcelable(CIPHER_DATABASE_KEY)
+ }
- var resultError = ""
- val resultException = result.exception
- val resultMessage = result.message
-
- if (resultException != null) {
- resultError = resultException.getLocalizedMessage(resources)
-
- when (resultException) {
- is DuplicateUuidDatabaseException -> {
- // Relaunch loading if we need to fix UUID
- showLoadDatabaseDuplicateUuidMessage {
-
- var databaseUri: Uri? = null
- var mainCredential = MainCredential()
- var readOnly = true
- var cipherEncryptDatabase: CipherEncryptDatabase? = null
-
- result.data?.let { resultData ->
- databaseUri = resultData.getParcelable(DATABASE_URI_KEY)
- mainCredential =
- resultData.getParcelable(MAIN_CREDENTIAL_KEY)
- ?: mainCredential
- readOnly = resultData.getBoolean(READ_ONLY_KEY)
- cipherEncryptDatabase =
- resultData.getParcelable(CIPHER_DATABASE_KEY)
- }
-
- databaseUri?.let { databaseFileUri ->
- showProgressDialogAndLoadDatabase(
- databaseFileUri,
- mainCredential,
- readOnly,
- cipherEncryptDatabase,
- true
- )
- }
+ databaseUri?.let { databaseFileUri ->
+ showProgressDialogAndLoadDatabase(
+ databaseFileUri,
+ mainCredential,
+ readOnly,
+ cipherEncryptDatabase,
+ true
+ )
}
}
- is FileNotFoundDatabaseException -> {
- // Remove this default database inaccessible
- if (mDefaultDatabase) {
- mDatabaseFileViewModel.removeDefaultDatabase()
- }
+ }
+ is FileNotFoundDatabaseException -> {
+ // Remove this default database inaccessible
+ if (mDefaultDatabase) {
+ mDatabaseFileViewModel.removeDefaultDatabase()
}
}
}
-
- // Show error message
- if (resultMessage != null && resultMessage.isNotEmpty()) {
- resultError = "$resultError $resultMessage"
- }
- Log.e(TAG, resultError)
- Snackbar.make(
- coordinatorLayout,
- resultError,
- Snackbar.LENGTH_LONG
- ).asError().show()
}
}
}
+ coordinatorLayout.showActionErrorIfNeeded(result)
}
private fun getUriFromIntent(intent: Intent?) {
// If is a view intent
val action = intent?.action
- if (action != null
- && action == VIEW_INTENT) {
- mDatabaseFileUri = intent.data
- mainCredentialView?.populateKeyFileTextView(UriUtil.getUriFromIntent(intent, KEY_KEYFILE))
+ if (action == VIEW_INTENT) {
+ fillCredentials(
+ intent.data,
+ UriUtil.getUriFromIntent(intent, KEY_KEYFILE),
+ HardwareKey.getHardwareKeyFromString(intent.getStringExtra(KEY_HARDWARE_KEY))
+ )
} else {
- mDatabaseFileUri = intent?.getParcelableExtra(KEY_FILENAME)
- intent?.getParcelableExtra(KEY_KEYFILE)?.let {
- mainCredentialView?.populateKeyFileTextView(it)
- }
+ fillCredentials(
+ intent?.getParcelableExtra(KEY_FILENAME),
+ intent?.getParcelableExtra(KEY_KEYFILE),
+ HardwareKey.getHardwareKeyFromString(intent?.getStringExtra(KEY_HARDWARE_KEY))
+ )
}
try {
intent?.removeExtra(KEY_KEYFILE)
+ intent?.removeExtra(KEY_HARDWARE_KEY)
} catch (e: Exception) {}
mDatabaseFileUri?.let {
mDatabaseFileViewModel.checkIfIsDefaultDatabase(it)
}
}
+ private fun fillCredentials(databaseUri: Uri?,
+ keyFileUri: Uri?,
+ hardwareKey: HardwareKey?) {
+ mDatabaseFileUri = databaseUri
+ mainCredentialView?.populateKeyFileView(keyFileUri)
+ mainCredentialView?.populateHardwareKeyView(hardwareKey)
+ }
+
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
getUriFromIntent(intent)
@@ -358,7 +379,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private fun launchGroupActivityIfLoaded(database: Database) {
// Check if database really loaded
if (database.loaded) {
- clearCredentialsViews(true)
+ clearCredentialsViews(clearKeyFile = true, clearHardwareKey = true)
GroupActivity.launch(this,
database,
{ onValidateSpecialMode() },
@@ -408,7 +429,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
val mainCredential = mainCredentialView?.getMainCredential() ?: MainCredential()
when (cipherDecryptDatabase.credentialStorage) {
CredentialStorage.PASSWORD -> {
- mainCredential.masterPassword = String(cipherDecryptDatabase.decryptedValue)
+ mainCredential.password = String(cipherDecryptDatabase.decryptedValue)
}
CredentialStorage.KEY_FILE -> {
// TODO advanced unlock key file
@@ -423,14 +444,23 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
)
}
- private fun onDatabaseFileLoaded(databaseFileUri: Uri?, keyFileUri: Uri?) {
+ private fun onDatabaseFileLoaded(databaseFileUri: Uri?,
+ keyFileUri: Uri?,
+ hardwareKey: HardwareKey?) {
// Define Key File text
if (mRememberKeyFile) {
- mainCredentialView?.populateKeyFileTextView(keyFileUri)
+ mainCredentialView?.populateKeyFileView(keyFileUri)
+ }
+
+ // Define hardware key
+ if (mRememberHardwareKey) {
+ mainCredentialView?.populateHardwareKeyView(hardwareKey)
}
// Define listener for validate button
- confirmButtonView?.setOnClickListener { loadDatabase() }
+ confirmButtonView?.setOnClickListener {
+ mainCredentialView?.validateCredential()
+ }
// If Activity is launch with a password and want to open directly
val intent = intent
@@ -462,10 +492,14 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
}
}
- private fun clearCredentialsViews(clearKeyFile: Boolean = !mRememberKeyFile) {
+ private fun clearCredentialsViews(clearKeyFile: Boolean = !mRememberKeyFile,
+ clearHardwareKey: Boolean = !mRememberHardwareKey) {
mainCredentialView?.populatePasswordTextView(null)
if (clearKeyFile) {
- mainCredentialView?.populateKeyFileTextView(null)
+ mainCredentialView?.populateKeyFileView(null)
+ }
+ if (clearHardwareKey) {
+ mainCredentialView?.populateHardwareKeyView(null)
}
}
@@ -656,18 +690,24 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
private const val KEY_FILENAME = "fileName"
private const val KEY_KEYFILE = "keyFile"
+ private const val KEY_HARDWARE_KEY = "hardwareKey"
private const val VIEW_INTENT = "android.intent.action.VIEW"
private const val KEY_READ_ONLY = "KEY_READ_ONLY"
private const val KEY_PASSWORD = "password"
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
- private fun buildAndLaunchIntent(activity: Activity, databaseFile: Uri, keyFile: Uri?,
+ private fun buildAndLaunchIntent(activity: Activity,
+ databaseFile: Uri,
+ keyFile: Uri?,
+ hardwareKey: HardwareKey?,
intentBuildLauncher: (Intent) -> Unit) {
val intent = Intent(activity, MainCredentialActivity::class.java)
intent.putExtra(KEY_FILENAME, databaseFile)
if (keyFile != null)
intent.putExtra(KEY_KEYFILE, keyFile)
+ if (hardwareKey != null)
+ intent.putExtra(KEY_HARDWARE_KEY, hardwareKey.toString())
intentBuildLauncher.invoke(intent)
}
@@ -680,8 +720,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
@Throws(FileNotFoundException::class)
fun launch(activity: Activity,
databaseFile: Uri,
- keyFile: Uri?) {
- buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
+ keyFile: Uri?,
+ hardwareKey: HardwareKey?) {
+ buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
activity.startActivity(intent)
}
}
@@ -696,8 +737,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForSearchResult(activity: Activity,
databaseFile: Uri,
keyFile: Uri?,
+ hardwareKey: HardwareKey?,
searchInfo: SearchInfo) {
- buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
+ buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForSearchModeResult(
activity,
intent,
@@ -715,8 +757,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForSaveResult(activity: Activity,
databaseFile: Uri,
keyFile: Uri?,
+ hardwareKey: HardwareKey?,
searchInfo: SearchInfo) {
- buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
+ buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForSaveModeResult(
activity,
intent,
@@ -734,8 +777,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForKeyboardResult(activity: Activity,
databaseFile: Uri,
keyFile: Uri?,
+ hardwareKey: HardwareKey?,
searchInfo: SearchInfo?) {
- buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
+ buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForKeyboardSelectionModeResult(
activity,
intent,
@@ -754,10 +798,11 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForAutofillResult(activity: AppCompatActivity,
databaseFile: Uri,
keyFile: Uri?,
+ hardwareKey: HardwareKey?,
activityResultLauncher: ActivityResultLauncher?,
autofillComponent: AutofillComponent,
searchInfo: SearchInfo?) {
- buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
+ buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
AutofillHelper.startActivityForAutofillResult(
activity,
intent,
@@ -775,8 +820,9 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launchForRegistration(activity: Activity,
databaseFile: Uri,
keyFile: Uri?,
+ hardwareKey: HardwareKey?,
registerInfo: RegisterInfo?) {
- buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
+ buildAndLaunchIntent(activity, databaseFile, keyFile, hardwareKey) { intent ->
EntrySelectionHelper.startActivityForRegistrationModeResult(
activity,
intent,
@@ -792,6 +838,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
fun launch(activity: AppCompatActivity,
databaseUri: Uri,
keyFile: Uri?,
+ hardwareKey: HardwareKey?,
fileNoFoundAction: (exception: FileNotFoundException) -> Unit,
onCancelSpecialMode: () -> Unit,
onLaunchActivitySpecialMode: () -> Unit,
@@ -800,43 +847,67 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
try {
EntrySelectionHelper.doSpecialAction(activity.intent,
{
- MainCredentialActivity.launch(activity,
- databaseUri, keyFile)
+ launch(
+ activity,
+ databaseUri,
+ keyFile,
+ hardwareKey
+ )
},
{ searchInfo -> // Search Action
- MainCredentialActivity.launchForSearchResult(activity,
- databaseUri, keyFile,
- searchInfo)
+ launchForSearchResult(
+ activity,
+ databaseUri,
+ keyFile,
+ hardwareKey,
+ searchInfo
+ )
onLaunchActivitySpecialMode()
},
{ searchInfo -> // Save Action
- MainCredentialActivity.launchForSaveResult(activity,
- databaseUri, keyFile,
- searchInfo)
+ launchForSaveResult(
+ activity,
+ databaseUri,
+ keyFile,
+ hardwareKey,
+ searchInfo
+ )
onLaunchActivitySpecialMode()
},
{ searchInfo -> // Keyboard Selection Action
- MainCredentialActivity.launchForKeyboardResult(activity,
- databaseUri, keyFile,
- searchInfo)
+ launchForKeyboardResult(
+ activity,
+ databaseUri,
+ keyFile,
+ hardwareKey,
+ searchInfo
+ )
onLaunchActivitySpecialMode()
},
{ searchInfo, autofillComponent -> // Autofill Selection Action
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- MainCredentialActivity.launchForAutofillResult(activity,
- databaseUri, keyFile,
- autofillActivityResultLauncher,
- autofillComponent,
- searchInfo)
+ launchForAutofillResult(
+ activity,
+ databaseUri,
+ keyFile,
+ hardwareKey,
+ autofillActivityResultLauncher,
+ autofillComponent,
+ searchInfo
+ )
onLaunchActivitySpecialMode()
} else {
onCancelSpecialMode()
}
},
{ registerInfo -> // Registration Action
- MainCredentialActivity.launchForRegistration(activity,
- databaseUri, keyFile,
- registerInfo)
+ launchForRegistration(
+ activity,
+ databaseUri,
+ keyFile,
+ hardwareKey,
+ registerInfo
+ )
onLaunchActivitySpecialMode()
}
)
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt
index d45c3ea97..dea4f73c9 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/MainCredentialDialogFragment.kt
@@ -27,7 +27,7 @@ import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.view.MainCredentialView
@@ -95,7 +95,7 @@ class MainCredentialDialogFragment : DatabaseDialogFragment() {
mExternalFileHelper = ExternalFileHelper(this)
mExternalFileHelper?.buildOpenDocument { uri ->
if (uri != null) {
- mainCredentialView?.populateKeyFileTextView(uri)
+ mainCredentialView?.populateKeyFileView(uri)
}
}
mainCredentialView?.setOpenKeyfileClickListener(mExternalFileHelper)
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 df32460cb..745543d3d 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
@@ -26,7 +26,7 @@ import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.R
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
class PasswordEncodingDialogFragment : DialogFragment() {
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ProFeatureDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ProFeatureDialogFragment.kt
index dcd70366c..ac0293b63 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ProFeatureDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/ProFeatureDialogFragment.kt
@@ -45,13 +45,16 @@ class ProFeatureDialogFragment : DialogFragment() {
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_ad_free), FROM_HTML_MODE_LEGACY)).append("\n\n")
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_buy_pro), FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.download) { _, _ ->
- UriUtil.gotoUrl(requireContext(), R.string.app_pro_url)
+ UriUtil.gotoUrl(activity,
+ activity.getString(R.string.play_store_url,
+ activity.getString(R.string.keepro_app_id))
+ )
}
} else {
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_feature_generosity), FROM_HTML_MODE_LEGACY)).append("\n\n")
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_donation), FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.contribute) { _, _ ->
- UriUtil.gotoUrl(requireContext(), R.string.contribution_url)
+ UriUtil.gotoUrl(activity, R.string.contribution_url)
}
}
builder.setMessage(stringBuilder)
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt
index e654c0817..4ba21490e 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SetMainCredentialDialogFragment.kt
@@ -35,9 +35,12 @@ import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.hardware.HardwareKey
+import com.kunzisoft.keepass.hardware.HardwareKeyActivity
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.password.PasswordEntropy
import com.kunzisoft.keepass.utils.UriUtil
+import com.kunzisoft.keepass.view.HardwareKeySelectionView
import com.kunzisoft.keepass.view.KeyFileSelectionView
import com.kunzisoft.keepass.view.PassKeyView
import com.kunzisoft.keepass.view.applyFontVisibility
@@ -45,18 +48,21 @@ import com.kunzisoft.keepass.view.applyFontVisibility
class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
private var mMasterPassword: String? = null
- private var mKeyFile: Uri? = null
+ private var mKeyFileUri: Uri? = null
+ private var mHardwareKey: HardwareKey? = null
- private var rootView: View? = null
+ private lateinit var rootView: View
- private var passwordCheckBox: CompoundButton? = null
+ private lateinit var passwordCheckBox: CompoundButton
+ private lateinit var passwordView: PassKeyView
+ private lateinit var passwordRepeatTextInputLayout: TextInputLayout
+ private lateinit var passwordRepeatView: TextView
- private var passKeyView: PassKeyView? = null
- private var passwordRepeatTextInputLayout: TextInputLayout? = null
- private var passwordRepeatView: TextView? = null
+ private lateinit var keyFileCheckBox: CompoundButton
+ private lateinit var keyFileSelectionView: KeyFileSelectionView
- private var keyFileCheckBox: CompoundButton? = null
- private var keyFileSelectionView: KeyFileSelectionView? = null
+ private lateinit var hardwareKeyCheckBox: CompoundButton
+ private lateinit var hardwareKeySelectionView: HardwareKeySelectionView
private var mListener: AssignMainCredentialDialogListener? = null
@@ -67,13 +73,15 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
private var mNoKeyConfirmationDialog: AlertDialog? = null
private var mEmptyKeyFileConfirmationDialog: AlertDialog? = null
+ private var mAllowNoMasterKey: Boolean = false
+
private val passwordTextWatcher = object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) {
- passwordCheckBox?.isChecked = true
+ passwordCheckBox.isChecked = true
}
}
@@ -113,10 +121,9 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
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)
+ mAllowNoMasterKey = getBoolean(ALLOW_NO_MASTER_KEY_ARG, false)
}
val builder = AlertDialog.Builder(activity)
@@ -128,63 +135,63 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
- rootView?.findViewById(R.id.credentials_information)?.setOnClickListener {
+ rootView.findViewById(R.id.credentials_information)?.setOnClickListener {
UriUtil.gotoUrl(activity, R.string.credentials_explanation_url)
}
- passwordCheckBox = rootView?.findViewById(R.id.password_checkbox)
- passKeyView = rootView?.findViewById(R.id.password_view)
- passwordRepeatTextInputLayout = rootView?.findViewById(R.id.password_repeat_input_layout)
- passwordRepeatView = rootView?.findViewById(R.id.password_confirmation)
- passwordRepeatView?.applyFontVisibility()
+ passwordCheckBox = rootView.findViewById(R.id.password_checkbox)
+ passwordView = rootView.findViewById(R.id.password_view)
+ passwordRepeatTextInputLayout = rootView.findViewById(R.id.password_repeat_input_layout)
+ passwordRepeatView = rootView.findViewById(R.id.password_confirmation)
+ passwordRepeatView.applyFontVisibility()
+
+ keyFileCheckBox = rootView.findViewById(R.id.keyfile_checkbox)
+ keyFileSelectionView = rootView.findViewById(R.id.keyfile_selection)
- keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox)
- keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection)
+ hardwareKeyCheckBox = rootView.findViewById(R.id.hardware_key_checkbox)
+ hardwareKeySelectionView = rootView.findViewById(R.id.hardware_key_selection)
mExternalFileHelper = ExternalFileHelper(this)
mExternalFileHelper?.buildOpenDocument { uri ->
uri?.let { pathUri ->
UriUtil.getFileData(requireContext(), uri)?.length()?.let { lengthFile ->
- keyFileSelectionView?.error = null
- keyFileCheckBox?.isChecked = true
- keyFileSelectionView?.uri = pathUri
+ keyFileSelectionView.error = null
+ keyFileCheckBox.isChecked = true
+ keyFileSelectionView.uri = pathUri
if (lengthFile <= 0L) {
showEmptyKeyFileConfirmationDialog()
}
}
}
}
- keyFileSelectionView?.setOpenDocumentClickListener(mExternalFileHelper)
+ keyFileSelectionView.setOpenDocumentClickListener(mExternalFileHelper)
+
+ hardwareKeySelectionView.selectionListener = { hardwareKey ->
+ hardwareKeyCheckBox.isChecked = true
+ hardwareKeySelectionView.error =
+ if (!HardwareKeyActivity.isHardwareKeyAvailable(requireActivity(), hardwareKey)) {
+ // show hardware driver dialog if required
+ getString(R.string.error_driver_required, hardwareKey.toString())
+ } else {
+ null
+ }
+ }
val dialog = builder.create()
+ dialog.setOnShowListener { dialog1 ->
+ val positiveButton = (dialog1 as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE)
+ positiveButton.setOnClickListener {
- if (passwordCheckBox != null && keyFileCheckBox!= null) {
- dialog.setOnShowListener { dialog1 ->
- val positiveButton = (dialog1 as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE)
- positiveButton.setOnClickListener {
-
- mMasterPassword = ""
- mKeyFile = null
-
- var error = verifyPassword() || verifyKeyFile()
- if (!passwordCheckBox!!.isChecked && !keyFileCheckBox!!.isChecked) {
- error = true
- if (allowNoMasterKey)
- showNoKeyConfirmationDialog()
- else {
- passwordRepeatTextInputLayout?.error = getString(R.string.error_disallow_no_credentials)
- }
- }
- if (!error) {
- mListener?.onAssignKeyDialogPositiveClick(retrieveMainCredential())
- dismiss()
- }
- }
- val negativeButton = dialog1.getButton(DialogInterface.BUTTON_NEGATIVE)
- negativeButton.setOnClickListener {
- mListener?.onAssignKeyDialogNegativeClick(retrieveMainCredential())
- dismiss()
- }
+ mMasterPassword = ""
+ mKeyFileUri = null
+ mHardwareKey = null
+
+ approveMainCredential()
+ }
+ val negativeButton = dialog1.getButton(DialogInterface.BUTTON_NEGATIVE)
+ negativeButton.setOnClickListener {
+ mListener?.onAssignKeyDialogNegativeClick(retrieveMainCredential())
+ dismiss()
}
}
@@ -194,78 +201,122 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
return super.onCreateDialog(savedInstanceState)
}
- private fun retrieveMainCredential(): MainCredential {
- val masterPassword = if (passwordCheckBox!!.isChecked) mMasterPassword else null
- val keyFile = if (keyFileCheckBox!!.isChecked) mKeyFile else null
- return MainCredential(masterPassword, keyFile)
- }
-
- override fun onResume() {
- super.onResume()
-
- // To check checkboxes if a text is present
- passKeyView?.addTextChangedListener(passwordTextWatcher)
- }
-
- override fun onPause() {
- super.onPause()
-
- passKeyView?.removeTextChangedListener(passwordTextWatcher)
+ private fun approveMainCredential() {
+ val errorPassword = verifyPassword()
+ val errorKeyFile = verifyKeyFile()
+ val errorHardwareKey = verifyHardwareKey()
+ // Check all to fill error
+ var error = errorPassword || errorKeyFile || errorHardwareKey
+ val hardwareKey = hardwareKeySelectionView.hardwareKey
+ if (!error
+ && (!passwordCheckBox.isChecked)
+ && (!keyFileCheckBox.isChecked)
+ && (!hardwareKeyCheckBox.isChecked)
+ ) {
+ error = true
+ if (mAllowNoMasterKey) {
+ // show no key dialog if required
+ showNoKeyConfirmationDialog()
+ } else {
+ passwordRepeatTextInputLayout.error =
+ getString(R.string.error_disallow_no_credentials)
+ }
+ } else if (!error
+ && mMasterPassword.isNullOrEmpty()
+ && !keyFileCheckBox.isChecked
+ && !hardwareKeyCheckBox.isChecked
+ ) {
+ // show empty password dialog if required
+ error = true
+ showEmptyPasswordConfirmationDialog()
+ } else if (!error
+ && hardwareKey != null
+ && !HardwareKeyActivity.isHardwareKeyAvailable(
+ requireActivity(), hardwareKey, false)
+ ) {
+ // show hardware driver dialog if required
+ error = true
+ hardwareKeySelectionView.error =
+ getString(R.string.error_driver_required, hardwareKey.toString())
+ }
+ if (!error) {
+ mListener?.onAssignKeyDialogPositiveClick(retrieveMainCredential())
+ dismiss()
+ }
}
private fun verifyPassword(): Boolean {
var error = false
- if (passwordCheckBox != null
- && passwordCheckBox!!.isChecked
- && passKeyView != null
- && passwordRepeatView != null) {
- mMasterPassword = passKeyView!!.passwordString
- val confPassword = passwordRepeatView!!.text.toString()
+ passwordRepeatTextInputLayout.error = null
+ if (passwordCheckBox.isChecked) {
+ mMasterPassword = passwordView.passwordString
+ val confPassword = passwordRepeatView.text.toString()
// Verify that passwords match
if (mMasterPassword != confPassword) {
error = true
// Passwords do not match
- passwordRepeatTextInputLayout?.error = getString(R.string.error_pass_match)
+ passwordRepeatTextInputLayout.error = getString(R.string.error_pass_match)
}
+ }
+ return error
+ }
- if ((mMasterPassword == null
- || mMasterPassword!!.isEmpty())
- && (keyFileCheckBox == null
- || !keyFileCheckBox!!.isChecked
- || keyFileSelectionView?.uri == null)) {
+ private fun verifyKeyFile(): Boolean {
+ var error = false
+ keyFileSelectionView.error = null
+ if (keyFileCheckBox.isChecked) {
+ keyFileSelectionView.uri?.let { uri ->
+ mKeyFileUri = uri
+ } ?: run {
error = true
- showEmptyPasswordConfirmationDialog()
+ keyFileSelectionView.error = getString(R.string.error_nokeyfile)
}
}
-
return error
}
- private fun verifyKeyFile(): Boolean {
+ private fun verifyHardwareKey(): Boolean {
var error = false
- if (keyFileCheckBox != null
- && keyFileCheckBox!!.isChecked) {
-
- keyFileSelectionView?.uri?.let { uri ->
- mKeyFile = uri
+ hardwareKeySelectionView.error = null
+ if (hardwareKeyCheckBox.isChecked) {
+ hardwareKeySelectionView.hardwareKey?.let { hardwareKey ->
+ mHardwareKey = hardwareKey
} ?: run {
error = true
- keyFileSelectionView?.error = getString(R.string.error_nokeyfile)
+ hardwareKeySelectionView.error = getString(R.string.error_no_hardware_key)
}
}
return error
}
+ private fun retrieveMainCredential(): MainCredential {
+ val masterPassword = if (passwordCheckBox.isChecked) mMasterPassword else null
+ val keyFileUri = if (keyFileCheckBox.isChecked) mKeyFileUri else null
+ val hardwareKey = if (hardwareKeyCheckBox.isChecked) mHardwareKey else null
+ return MainCredential(masterPassword, keyFileUri, hardwareKey)
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ // To check checkboxes if a text is present
+ passwordView.addTextChangedListener(passwordTextWatcher)
+ }
+
+ override fun onPause() {
+ super.onPause()
+
+ passwordView.removeTextChangedListener(passwordTextWatcher)
+ }
+
private fun showEmptyPasswordConfirmationDialog() {
activity?.let {
val builder = AlertDialog.Builder(it)
builder.setMessage(R.string.warning_empty_password)
.setPositiveButton(android.R.string.ok) { _, _ ->
- if (!verifyKeyFile()) {
- mListener?.onAssignKeyDialogPositiveClick(retrieveMainCredential())
- this@SetMainCredentialDialogFragment.dismiss()
- }
+ mListener?.onAssignKeyDialogPositiveClick(retrieveMainCredential())
+ this@SetMainCredentialDialogFragment.dismiss()
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
mEmptyPasswordConfirmationDialog = builder.create()
@@ -299,8 +350,8 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
})
.setPositiveButton(android.R.string.ok) { _, _ -> }
.setNegativeButton(android.R.string.cancel) { _, _ ->
- keyFileCheckBox?.isChecked = false
- keyFileSelectionView?.uri = null
+ keyFileCheckBox.isChecked = false
+ keyFileSelectionView.uri = null
}
mEmptyKeyFileConfirmationDialog = builder.create()
mEmptyKeyFileConfirmationDialog?.show()
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnderDevelopmentFeatureDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnderDevelopmentFeatureDialogFragment.kt
index 57fffb0d5..fee8b6476 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnderDevelopmentFeatureDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/UnderDevelopmentFeatureDialogFragment.kt
@@ -39,6 +39,7 @@ class UnderDevelopmentFeatureDialogFragment : DialogFragment() {
val builder = AlertDialog.Builder(activity)
val stringBuilder = SpannableStringBuilder()
+ /*
if (UriUtil.contributingUser(activity)) {
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_thanks), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(HtmlCompat.fromHtml(getString(R.string.html_rose), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
@@ -46,14 +47,14 @@ class UnderDevelopmentFeatureDialogFragment : DialogFragment() {
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_upgrade), HtmlCompat.FROM_HTML_MODE_LEGACY)).append(" ")
builder.setPositiveButton(android.R.string.ok) { _, _ -> dismiss() }
} else {
+ */
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_contibute), HtmlCompat.FROM_HTML_MODE_LEGACY)).append(" ")
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_encourage), HtmlCompat.FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.contribute) { _, _ ->
UriUtil.gotoUrl(requireContext(), R.string.contribution_url)
}
- builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
- }
+ //}
builder.setMessage(stringBuilder)
// Create the AlertDialog object and return it
return builder.create()
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt
index cc12c2347..2a4ae25cf 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt
@@ -56,7 +56,7 @@ class ExternalFileHelper {
fun buildOpenDocument(onFileSelected: ((uri: Uri?) -> Unit)?) {
- val resultCallback = ActivityResultCallback { result ->
+ val resultCallback = ActivityResultCallback { result ->
result?.let { uri ->
UriUtil.takeUriPermission(activity?.contentResolver, uri)
onFileSelected?.invoke(uri)
@@ -91,7 +91,7 @@ class ExternalFileHelper {
fun buildCreateDocument(typeString: String = "application/octet-stream",
onFileCreated: (fileCreated: Uri?)->Unit) {
- val resultCallback = ActivityResultCallback { result ->
+ val resultCallback = ActivityResultCallback { result ->
onFileCreated.invoke(result)
}
@@ -150,7 +150,7 @@ class ExternalFileHelper {
class OpenDocument : ActivityResultContracts.OpenDocument() {
@SuppressLint("InlinedApi")
- override fun createIntent(context: Context, input: Array): Intent {
+ override fun createIntent(context: Context, input: Array): Intent {
return super.createIntent(context, input).apply {
addCategory(Intent.CATEGORY_OPENABLE)
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
@@ -178,11 +178,10 @@ class ExternalFileHelper {
}
}
- class CreateDocument(private val typeString: String) : ActivityResultContracts.CreateDocument() {
+ class CreateDocument(typeString: String) : ActivityResultContracts.CreateDocument(typeString) {
override fun createIntent(context: Context, input: String): Intent {
return super.createIntent(context, input).apply {
addCategory(Intent.CATEGORY_OPENABLE)
- type = typeString
}
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt
index c1d2e72b9..b9be0ffc4 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseActivity.kt
@@ -6,8 +6,8 @@ import androidx.activity.viewModels
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.action.DatabaseTaskProvider
import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.model.CipherEncryptDatabase
-import com.kunzisoft.keepass.model.MainCredential
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
@@ -20,7 +20,7 @@ abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- mDatabaseTaskProvider = DatabaseTaskProvider(this)
+ mDatabaseTaskProvider = DatabaseTaskProvider(this, showDatabaseDialog())
mDatabaseTaskProvider?.onDatabaseRetrieved = { database ->
val databaseWasReloaded = database?.wasReloaded == true
@@ -36,6 +36,17 @@ abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
}
}
+ protected open fun showDatabaseDialog(): Boolean {
+ return true
+ }
+
+ override fun onDestroy() {
+ mDatabaseTaskProvider?.destroy()
+ mDatabaseTaskProvider = null
+ mDatabase = null
+ super.onDestroy()
+ }
+
override fun onDatabaseRetrieved(database: Database?) {
mDatabase = database
mDatabaseViewModel.defineDatabase(database)
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt
index 0e55420a7..0f3705ace 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/legacy/DatabaseLockActivity.kt
@@ -32,7 +32,6 @@ import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import com.kunzisoft.keepass.R
-import com.kunzisoft.keepass.activities.dialogs.DatabaseDialogFragment
import com.kunzisoft.keepass.activities.dialogs.DeleteNodesDialogFragment
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
@@ -44,7 +43,7 @@ import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.model.GroupInfo
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
@@ -91,8 +90,8 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
mDatabaseTaskProvider?.startDatabaseSave(save)
}
- mDatabaseViewModel.mergeDatabase.observe(this) {
- mDatabaseTaskProvider?.startDatabaseMerge()
+ mDatabaseViewModel.mergeDatabase.observe(this) { save ->
+ mDatabaseTaskProvider?.startDatabaseMerge(save)
}
mDatabaseViewModel.reloadDatabase.observe(this) { fixDuplicateUuid ->
@@ -228,6 +227,9 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
// Reload the current activity
if (result.isSuccess) {
reloadActivity()
+ if (actionTask == DatabaseTaskNotificationService.ACTION_DATABASE_MERGE_TASK) {
+ Toast.makeText(this, R.string.merge_success, Toast.LENGTH_LONG).show()
+ }
} else {
this.showActionErrorIfNeeded(result)
finish()
@@ -271,11 +273,11 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
}
fun mergeDatabase() {
- mDatabaseTaskProvider?.startDatabaseMerge()
+ mDatabaseTaskProvider?.startDatabaseMerge(mAutoSaveEnable)
}
fun mergeDatabaseFrom(uri: Uri, mainCredential: MainCredential) {
- mDatabaseTaskProvider?.startDatabaseMerge(uri, mainCredential)
+ mDatabaseTaskProvider?.startDatabaseMerge(mAutoSaveEnable, uri, mainCredential)
}
fun reloadDatabase() {
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt
index 32904c342..2b113d03b 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/StylishActivity.kt
@@ -21,14 +21,21 @@ package com.kunzisoft.keepass.activities.stylish
import android.content.ActivityNotFoundException
import android.content.Intent
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
-import android.view.WindowManager
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.view.WindowManager.LayoutParams.FLAG_SECURE
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
+import androidx.preference.PreferenceManager
+import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.settings.NestedAppSettingsFragment.Companion.DATABASE_PREFERENCE_CHANGED
+import com.kunzisoft.keepass.settings.PreferencesUtil
/**
* Stylish Hide Activity that apply a dynamic style and sets FLAG_SECURE to prevent screenshots / from
@@ -81,8 +88,24 @@ abstract class StylishActivity : AppCompatActivity() {
setTheme(themeId)
}
+ PreferenceManager.getDefaultSharedPreferences(this)
+ .registerOnSharedPreferenceChangeListener(onScreenshotModePrefListener)
+ }
+ private val onScreenshotModePrefListener = OnSharedPreferenceChangeListener { _, key ->
+ if (key != getString(R.string.enable_screenshot_mode_key)) return@OnSharedPreferenceChangeListener
+
+ setScreenshotMode(PreferencesUtil.isScreenshotModeEnabled(this))
+ }
+
+ private fun setScreenshotMode(isEnabled: Boolean) {
+ findViewById(R.id.screenshot_mode_banner)?.visibility = if (isEnabled) VISIBLE else GONE
+
// Several gingerbread devices have problems with FLAG_SECURE
- window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
+ if (isEnabled) {
+ window.clearFlags(FLAG_SECURE)
+ } else {
+ window.setFlags(FLAG_SECURE, FLAG_SECURE)
+ }
}
override fun onResume() {
@@ -94,6 +117,7 @@ abstract class StylishActivity : AppCompatActivity() {
Log.d(this.javaClass.name, "Theme change detected, restarting activity")
recreateActivity()
}
+ setScreenshotMode(PreferencesUtil.isScreenshotModeEnabled(this))
}
private fun recreateActivity() {
diff --git a/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt b/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt
index 3597ceea4..16d1a190c 100644
--- a/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt
@@ -23,8 +23,15 @@ import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import android.content.Context
+import androidx.room.AutoMigration
-@Database(version = 1, entities = [FileDatabaseHistoryEntity::class, CipherDatabaseEntity::class])
+@Database(
+ version = 2,
+ entities = [FileDatabaseHistoryEntity::class, CipherDatabaseEntity::class],
+ autoMigrations = [
+ AutoMigration (from = 1, to = 2)
+ ]
+)
abstract class AppDatabase : RoomDatabase() {
abstract fun fileDatabaseHistoryDao(): FileDatabaseHistoryDao
diff --git a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt
index 0785ec45e..cce17c36d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.app.database
import android.content.Context
import android.net.Uri
import android.util.Log
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.SingletonHolderParameter
@@ -44,6 +45,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
DatabaseFile(
databaseUri,
UriUtil.parse(fileDatabaseHistoryEntity?.keyFileUri),
+ HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey),
UriUtil.decode(fileDatabaseHistoryEntity?.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""),
fileDatabaseInfo.exists,
@@ -85,13 +87,14 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
|| !hideBrokenLocations) {
databaseFileListLoaded.add(
DatabaseFile(
- UriUtil.parse(fileDatabaseHistoryEntity.databaseUri),
- UriUtil.parse(fileDatabaseHistoryEntity.keyFileUri),
- UriUtil.decode(fileDatabaseHistoryEntity.databaseUri),
- fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
- fileDatabaseInfo.exists,
- fileDatabaseInfo.getLastModificationString(),
- fileDatabaseInfo.getSizeString()
+ UriUtil.parse(fileDatabaseHistoryEntity.databaseUri),
+ UriUtil.parse(fileDatabaseHistoryEntity.keyFileUri),
+ HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey),
+ UriUtil.decode(fileDatabaseHistoryEntity.databaseUri),
+ fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
+ fileDatabaseInfo.exists,
+ fileDatabaseInfo.getLastModificationString(),
+ fileDatabaseInfo.getSizeString()
)
)
}
@@ -107,11 +110,14 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
).execute()
}
- fun addOrUpdateDatabaseUri(databaseUri: Uri, keyFileUri: Uri? = null,
+ fun addOrUpdateDatabaseUri(databaseUri: Uri,
+ keyFileUri: Uri? = null,
+ hardwareKey: HardwareKey? = null,
databaseFileAddedOrUpdatedResult: ((DatabaseFile?) -> Unit)? = null) {
addOrUpdateDatabaseFile(DatabaseFile(
- databaseUri,
- keyFileUri
+ databaseUri,
+ keyFileUri,
+ hardwareKey
), databaseFileAddedOrUpdatedResult)
}
@@ -130,6 +136,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
?: fileDatabaseHistoryRetrieve?.databaseAlias
?: "",
databaseFileToAddOrUpdate.keyFileUri?.toString(),
+ databaseFileToAddOrUpdate.hardwareKey?.value,
System.currentTimeMillis()
)
@@ -147,13 +154,14 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
val fileDatabaseInfo = FileDatabaseInfo(applicationContext,
fileDatabaseHistory.databaseUri)
DatabaseFile(
- UriUtil.parse(fileDatabaseHistory.databaseUri),
- UriUtil.parse(fileDatabaseHistory.keyFileUri),
- UriUtil.decode(fileDatabaseHistory.databaseUri),
- fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
- fileDatabaseInfo.exists,
- fileDatabaseInfo.getLastModificationString(),
- fileDatabaseInfo.getSizeString()
+ UriUtil.parse(fileDatabaseHistory.databaseUri),
+ UriUtil.parse(fileDatabaseHistory.keyFileUri),
+ HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
+ UriUtil.decode(fileDatabaseHistory.databaseUri),
+ fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
+ fileDatabaseInfo.exists,
+ fileDatabaseInfo.getLastModificationString(),
+ fileDatabaseInfo.getSizeString()
)
}
},
@@ -172,10 +180,11 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
val returnValue = databaseFileHistoryDao.delete(fileDatabaseHistory)
if (returnValue > 0) {
DatabaseFile(
- UriUtil.parse(fileDatabaseHistory.databaseUri),
- UriUtil.parse(fileDatabaseHistory.keyFileUri),
- UriUtil.decode(fileDatabaseHistory.databaseUri),
- databaseFileToDelete.databaseAlias
+ UriUtil.parse(fileDatabaseHistory.databaseUri),
+ UriUtil.parse(fileDatabaseHistory.keyFileUri),
+ HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
+ UriUtil.decode(fileDatabaseHistory.databaseUri),
+ databaseFileToDelete.databaseAlias
)
} else {
null
diff --git a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt
index 470bec74c..96fd5df08 100644
--- a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt
@@ -35,6 +35,9 @@ data class FileDatabaseHistoryEntity(
@ColumnInfo(name = "keyfile_uri")
var keyFileUri: String?,
+ @ColumnInfo(name = "hardware_key")
+ var hardwareKey: String?,
+
@ColumnInfo(name = "updated")
val updated: Long
) {
diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt
index 8a9e200fb..1370deb29 100644
--- a/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/biometric/AdvancedUnlockFragment.kt
@@ -34,12 +34,10 @@ import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
-import com.getkeepsafe.taptargetview.TapTargetView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.StylishFragment
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
-import com.kunzisoft.keepass.database.exception.IODatabaseException
-import com.kunzisoft.keepass.education.PasswordActivityEducation
+import com.kunzisoft.keepass.database.exception.UnknownDatabaseLocationException
import com.kunzisoft.keepass.model.CipherDecryptDatabase
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.CredentialStorage
@@ -398,7 +396,7 @@ class AdvancedUnlockFragment: StylishFragment(), AdvancedUnlockManager.AdvancedU
}
} ?: deleteEncryptedDatabaseKey()
}
- } ?: throw IODatabaseException()
+ } ?: throw UnknownDatabaseLocationException()
} ?: throw Exception("AdvancedUnlockManager not initialized")
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt
index f405faeb6..d61d83d69 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/AssignMainCredentialInDatabaseRunnable.kt
@@ -24,15 +24,16 @@ import android.net.Uri
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.model.MainCredential
-import com.kunzisoft.keepass.utils.UriUtil
+import com.kunzisoft.keepass.hardware.HardwareKey
+import com.kunzisoft.keepass.database.element.MainCredential
open class AssignMainCredentialInDatabaseRunnable (
- context: Context,
- database: Database,
- protected val mDatabaseUri: Uri,
- protected val mMainCredential: MainCredential)
- : SaveDatabaseRunnable(context, database, true) {
+ context: Context,
+ database: Database,
+ protected val mDatabaseUri: Uri,
+ mainCredential: MainCredential,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : SaveDatabaseRunnable(context, database, true, mainCredential, challengeResponseRetriever) {
private var mBackupKey: ByteArray? = null
@@ -40,10 +41,7 @@ open class AssignMainCredentialInDatabaseRunnable (
// Set key
try {
mBackupKey = ByteArray(database.masterKey.size)
- System.arraycopy(database.masterKey, 0, mBackupKey!!, 0, mBackupKey!!.size)
-
- val uriInputStream = UriUtil.getUriInputStream(context.contentResolver, mMainCredential.keyFileUri)
- database.assignMasterKey(mMainCredential.masterPassword, uriInputStream)
+ database.masterKey.copyInto(mBackupKey!!)
} catch (e: Exception) {
erase(mBackupKey)
setError(e)
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt
index e0c11577c..7707559da 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/CreateDatabaseRunnable.kt
@@ -24,7 +24,8 @@ import android.net.Uri
import android.util.Log
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.hardware.HardwareKey
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.settings.PreferencesUtil
class CreateDatabaseRunnable(context: Context,
@@ -33,9 +34,10 @@ class CreateDatabaseRunnable(context: Context,
private val databaseName: String,
private val rootName: String,
private val templateGroupName: String?,
- mainCredential: MainCredential,
+ val mainCredential: MainCredential,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
private val createDatabaseResult: ((Result) -> Unit)?)
- : AssignMainCredentialInDatabaseRunnable(context, mDatabase, databaseUri, mainCredential) {
+ : AssignMainCredentialInDatabaseRunnable(context, mDatabase, databaseUri, mainCredential, challengeResponseRetriever) {
override fun onStartRun() {
try {
@@ -58,8 +60,11 @@ class CreateDatabaseRunnable(context: Context,
// Add database to recent files
if (PreferencesUtil.rememberDatabaseLocations(context)) {
FileDatabaseHistoryAction.getInstance(context.applicationContext)
- .addOrUpdateDatabaseUri(mDatabaseUri,
- if (PreferencesUtil.rememberKeyFileLocations(context)) mMainCredential.keyFileUri else null)
+ .addOrUpdateDatabaseUri(
+ mDatabaseUri,
+ if (PreferencesUtil.rememberKeyFileLocations(context)) mainCredential.keyFileUri else null,
+ if (PreferencesUtil.rememberHardwareKey(context)) mainCredential.hardwareKey else null,
+ )
}
// Register the current time to init the lock timer
@@ -72,6 +77,9 @@ class CreateDatabaseRunnable(context: Context,
override fun onFinishRun() {
super.onFinishRun()
+ if (result.isSuccess) {
+ mDatabase.loaded = true
+ }
createDatabaseResult?.invoke(result)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt
index 8430bd816..ebf10bb03 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/DatabaseTaskProvider.kt
@@ -19,7 +19,6 @@
*/
package com.kunzisoft.keepass.database.action
-import android.app.Service
import android.content.*
import android.content.Context.*
import android.net.Uri
@@ -38,14 +37,16 @@ import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.Group
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.model.CipherEncryptDatabase
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.model.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
+import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_CHALLENGE_RESPONDED
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_ASSIGN_PASSWORD_TASK
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
@@ -89,11 +90,12 @@ import java.util.*
* Utility class to connect an activity or a service to the DatabaseTaskNotificationService,
* Useful to retrieve a database instance and sending tasks commands
*/
-class DatabaseTaskProvider {
+class DatabaseTaskProvider(private var context: Context,
+ private var showDialog: Boolean = true) {
- private var activity: FragmentActivity? = null
- private var service: Service? = null
- private var context: Context
+ // To show dialog only if context is an activity
+ private var activity: FragmentActivity? = try { context as? FragmentActivity? }
+ catch (_: Exception) { null }
var onDatabaseRetrieved: ((database: Database?) -> Unit)? = null
@@ -101,7 +103,10 @@ class DatabaseTaskProvider {
actionTask: String,
result: ActionRunnable.Result) -> Unit)? = null
- private var intentDatabaseTask: Intent
+ private var intentDatabaseTask: Intent = Intent(
+ context.applicationContext,
+ DatabaseTaskNotificationService::class.java
+ )
private var databaseTaskBroadcastReceiver: BroadcastReceiver? = null
private var mBinder: DatabaseTaskNotificationService.ActionTaskBinder? = null
@@ -111,30 +116,33 @@ class DatabaseTaskProvider {
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
- constructor(activity: FragmentActivity) {
- this.activity = activity
- this.context = activity
- this.intentDatabaseTask = Intent(activity.applicationContext,
- DatabaseTaskNotificationService::class.java)
- }
-
- constructor(service: Service) {
- this.service = service
- this.context = service
- this.intentDatabaseTask = Intent(service.applicationContext,
- DatabaseTaskNotificationService::class.java)
+ fun destroy() {
+ this.activity = null
+ this.onDatabaseRetrieved = null
+ this.onActionFinish = null
+ this.databaseTaskBroadcastReceiver = null
+ this.mBinder = null
+ this.serviceConnection = null
+ this.progressTaskDialogFragment = null
+ this.databaseChangedDialogFragment = null
}
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
- override fun onStartAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?) {
- startDialog(titleId, messageId, warningId)
+ override fun onStartAction(database: Database,
+ progressMessage: ProgressMessage) {
+ if (showDialog)
+ startDialog(progressMessage)
}
- override fun onUpdateAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?) {
- updateDialog(titleId, messageId, warningId)
+ override fun onUpdateAction(database: Database,
+ progressMessage: ProgressMessage) {
+ if (showDialog)
+ updateDialog(progressMessage)
}
- override fun onStopAction(database: Database, actionTask: String, result: ActionRunnable.Result) {
+ override fun onStopAction(database: Database,
+ actionTask: String,
+ result: ActionRunnable.Result) {
onActionFinish?.invoke(database, actionTask, result)
// Remove the progress task
stopDialog()
@@ -181,9 +189,7 @@ class DatabaseTaskProvider {
}
}
- private fun startDialog(titleId: Int? = null,
- messageId: Int? = null,
- warningId: Int? = null) {
+ private fun startDialog(progressMessage: ProgressMessage) {
activity?.let { activity ->
activity.lifecycleScope.launch {
if (progressTaskDialogFragment == null) {
@@ -197,22 +203,17 @@ class DatabaseTaskProvider {
PROGRESS_TASK_DIALOG_TAG
)
}
- updateDialog(titleId, messageId, warningId)
+ updateDialog(progressMessage)
}
}
}
- private fun updateDialog(titleId: Int?, messageId: Int?, warningId: Int?) {
+ private fun updateDialog(progressMessage: ProgressMessage) {
progressTaskDialogFragment?.apply {
- titleId?.let {
- updateTitle(it)
- }
- messageId?.let {
- updateMessage(it)
- }
- warningId?.let {
- updateWarning(it)
- }
+ updateTitle(progressMessage.titleId)
+ updateMessage(progressMessage.messageId)
+ updateWarning(progressMessage.warningId)
+ setCancellable(progressMessage.cancelable)
}
}
@@ -226,9 +227,7 @@ class DatabaseTaskProvider {
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) {
mBinder = (serviceBinder as DatabaseTaskNotificationService.ActionTaskBinder?)?.apply {
- addDatabaseListener(databaseListener)
- addDatabaseFileInfoListener(databaseInfoListener)
- addActionTaskListener(actionTaskListener)
+ addServiceListeners(this)
getService().checkDatabase()
getService().checkDatabaseInfo()
getService().checkAction()
@@ -236,15 +235,25 @@ class DatabaseTaskProvider {
}
override fun onServiceDisconnected(name: ComponentName?) {
- mBinder?.removeActionTaskListener(actionTaskListener)
- mBinder?.removeDatabaseFileInfoListener(databaseInfoListener)
- mBinder?.removeDatabaseListener(databaseListener)
+ removeServiceListeners(mBinder)
mBinder = null
}
}
}
}
+ private fun addServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) {
+ service?.addDatabaseListener(databaseListener)
+ service?.addDatabaseFileInfoListener(databaseInfoListener)
+ service?.addActionTaskListener(actionTaskListener)
+ }
+
+ private fun removeServiceListeners(service: DatabaseTaskNotificationService.ActionTaskBinder?) {
+ service?.removeActionTaskListener(actionTaskListener)
+ service?.removeDatabaseFileInfoListener(databaseInfoListener)
+ service?.removeDatabaseListener(databaseListener)
+ }
+
private fun bindService() {
initServiceConnection()
serviceConnection?.let {
@@ -262,10 +271,6 @@ class DatabaseTaskProvider {
serviceConnection = null
}
- fun isBinded(): Boolean {
- return mBinder != null
- }
-
fun registerProgressTask() {
stopDialog()
@@ -299,9 +304,7 @@ class DatabaseTaskProvider {
fun unregisterProgressTask() {
stopDialog()
- mBinder?.removeActionTaskListener(actionTaskListener)
- mBinder?.removeDatabaseFileInfoListener(databaseInfoListener)
- mBinder?.removeDatabaseListener(databaseListener)
+ removeServiceListeners(mBinder)
mBinder = null
unBindService()
@@ -321,7 +324,7 @@ class DatabaseTaskProvider {
context.startService(intentDatabaseTask)
} catch (e: Exception) {
Log.e(TAG, "Unable to perform database action", e)
- Toast.makeText(activity, R.string.error_start_database_action, Toast.LENGTH_LONG).show()
+ Toast.makeText(context, R.string.error_start_database_action, Toast.LENGTH_LONG).show()
}
}
@@ -332,7 +335,8 @@ class DatabaseTaskProvider {
*/
fun startDatabaseCreate(databaseUri: Uri,
- mainCredential: MainCredential) {
+ mainCredential: MainCredential
+ ) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri)
putParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY, mainCredential)
@@ -355,9 +359,11 @@ class DatabaseTaskProvider {
, ACTION_DATABASE_LOAD_TASK)
}
- fun startDatabaseMerge(fromDatabaseUri: Uri? = null,
+ fun startDatabaseMerge(save: Boolean,
+ fromDatabaseUri: Uri? = null,
mainCredential: MainCredential? = null) {
start(Bundle().apply {
+ putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, fromDatabaseUri)
putParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY, mainCredential)
}
@@ -385,7 +391,8 @@ class DatabaseTaskProvider {
}
fun startDatabaseAssignPassword(databaseUri: Uri,
- mainCredential: MainCredential) {
+ mainCredential: MainCredential
+ ) {
start(Bundle().apply {
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, databaseUri)
@@ -702,6 +709,13 @@ class DatabaseTaskProvider {
, ACTION_DATABASE_SAVE)
}
+ fun startChallengeResponded(response: ByteArray?) {
+ start(Bundle().apply {
+ putByteArray(DatabaseTaskNotificationService.DATA_BYTES, response)
+ }
+ , ACTION_CHALLENGE_RESPONDED)
+ }
+
companion object {
private val TAG = DatabaseTaskProvider::class.java.name
}
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 2329ff7f7..24c024721 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/LoadDatabaseRunnable.kt
@@ -25,9 +25,10 @@ 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.database.element.binary.BinaryData
-import com.kunzisoft.keepass.database.exception.LoadDatabaseException
+import com.kunzisoft.keepass.database.exception.DatabaseInputException
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.CipherEncryptDatabase
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
@@ -35,8 +36,9 @@ import com.kunzisoft.keepass.utils.UriUtil
class LoadDatabaseRunnable(private val context: Context,
private val mDatabase: Database,
- private val mUri: Uri,
+ private val mDatabaseUri: Uri,
private val mMainCredential: MainCredential,
+ private val mChallengeResponseRetriever: (hardwareKey: HardwareKey, seed: ByteArray?) -> ByteArray,
private val mReadonly: Boolean,
private val mCipherEncryptDatabase: CipherEncryptDatabase?,
private val mFixDuplicateUUID: Boolean,
@@ -51,18 +53,21 @@ class LoadDatabaseRunnable(private val context: Context,
override fun onActionRun() {
try {
- mDatabase.loadData(mUri,
- mMainCredential,
- mReadonly,
- context.contentResolver,
- UriUtil.getBinaryDir(context),
- { memoryWanted ->
- BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
- },
- mFixDuplicateUUID,
- progressTaskUpdater)
+ mDatabase.loadData(
+ context.contentResolver,
+ mDatabaseUri,
+ mMainCredential,
+ mChallengeResponseRetriever,
+ mReadonly,
+ UriUtil.getBinaryDir(context),
+ { memoryWanted ->
+ BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
+ },
+ mFixDuplicateUUID,
+ progressTaskUpdater
+ )
}
- catch (e: LoadDatabaseException) {
+ catch (e: DatabaseInputException) {
setError(e)
}
@@ -70,8 +75,11 @@ class LoadDatabaseRunnable(private val context: Context,
// Save keyFile in app database
if (PreferencesUtil.rememberDatabaseLocations(context)) {
FileDatabaseHistoryAction.getInstance(context)
- .addOrUpdateDatabaseUri(mUri,
- if (PreferencesUtil.rememberKeyFileLocations(context)) mMainCredential.keyFileUri else null)
+ .addOrUpdateDatabaseUri(
+ mDatabaseUri,
+ if (PreferencesUtil.rememberKeyFileLocations(context)) mMainCredential.keyFileUri else null,
+ if (PreferencesUtil.rememberHardwareKey(context)) mMainCredential.hardwareKey else null,
+ )
}
// Register the biometric
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt
index a0ee19616..2537b4edd 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/MergeDatabaseRunnable.kt
@@ -22,36 +22,43 @@ 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.element.MainCredential
import com.kunzisoft.keepass.database.element.binary.BinaryData
-import com.kunzisoft.keepass.database.exception.LoadDatabaseException
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.exception.DatabaseException
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.settings.PreferencesUtil
-import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
-class MergeDatabaseRunnable(private val context: Context,
- private val mDatabase: Database,
- private val mDatabaseToMergeUri: Uri?,
- private val mDatabaseToMergeMainCredential: MainCredential?,
- private val progressTaskUpdater: ProgressTaskUpdater?,
- private val mLoadDatabaseResult: ((Result) -> Unit)?)
- : ActionRunnable() {
+class MergeDatabaseRunnable(
+ context: Context,
+ private val mDatabaseToMergeUri: Uri?,
+ private val mDatabaseToMergeMainCredential: MainCredential?,
+ private val mDatabaseToMergeChallengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
+ database: Database,
+ saveDatabase: Boolean,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
+ private val progressTaskUpdater: ProgressTaskUpdater?,
+ private val mLoadDatabaseResult: ((Result) -> Unit)?)
+ : SaveDatabaseRunnable(context, database, saveDatabase, null, challengeResponseRetriever) {
override fun onStartRun() {
- mDatabase.wasReloaded = true
+ database.wasReloaded = true
+ super.onStartRun()
}
override fun onActionRun() {
try {
- mDatabase.mergeData(mDatabaseToMergeUri,
- mDatabaseToMergeMainCredential,
+ database.mergeData(
context.contentResolver,
+ mDatabaseToMergeUri,
+ mDatabaseToMergeMainCredential,
+ mDatabaseToMergeChallengeResponseRetriever,
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
progressTaskUpdater
)
- } catch (e: LoadDatabaseException) {
+ } catch (e: DatabaseException) {
setError(e)
}
@@ -59,9 +66,11 @@ class MergeDatabaseRunnable(private val context: Context,
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
}
+ super.onActionRun()
}
override fun onFinishRun() {
+ super.onFinishRun()
mLoadDatabaseResult?.invoke(result)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt
index 231c63d87..c382e63e7 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/ReloadDatabaseRunnable.kt
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.binary.BinaryData
-import com.kunzisoft.keepass.database.exception.LoadDatabaseException
+import com.kunzisoft.keepass.database.exception.DatabaseException
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
@@ -47,7 +47,7 @@ class ReloadDatabaseRunnable(private val context: Context,
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
progressTaskUpdater)
- } catch (e: LoadDatabaseException) {
+ } catch (e: DatabaseException) {
setError(e)
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/RemoveUnlinkedDataDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/RemoveUnlinkedDataDatabaseRunnable.kt
index 1e3da6cd7..3cc5bdb43 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/RemoveUnlinkedDataDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/RemoveUnlinkedDataDatabaseRunnable.kt
@@ -21,12 +21,14 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.hardware.HardwareKey
class RemoveUnlinkedDataDatabaseRunnable (
context: Context,
database: Database,
- saveDatabase: Boolean)
- : SaveDatabaseRunnable(context, database, saveDatabase) {
+ saveDatabase: Boolean,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : SaveDatabaseRunnable(context, database, saveDatabase, null, challengeResponseRetriever) {
override fun onActionRun() {
try {
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 3b88d51ed..c6ae0264a 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
@@ -23,11 +23,15 @@ import android.content.Context
import android.net.Uri
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.exception.DatabaseException
+import com.kunzisoft.keepass.hardware.HardwareKey
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.tasks.ActionRunnable
open class SaveDatabaseRunnable(protected var context: Context,
protected var database: Database,
private var saveDatabase: Boolean,
+ private var mainCredential: MainCredential?, // If null, uses composite Key
+ private var challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
private var databaseCopyUri: Uri? = null)
: ActionRunnable() {
@@ -39,7 +43,12 @@ open class SaveDatabaseRunnable(protected var context: Context,
database.checkVersion()
if (saveDatabase && result.isSuccess) {
try {
- database.saveData(databaseCopyUri, context.contentResolver)
+ database.saveData(
+ context.contentResolver,
+ context.cacheDir,
+ databaseCopyUri,
+ mainCredential,
+ challengeResponseRetriever)
} catch (e: DatabaseException) {
setError(e)
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/UpdateCompressionBinariesDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/UpdateCompressionBinariesDatabaseRunnable.kt
index 5b0e279c4..0c36983d7 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/UpdateCompressionBinariesDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/UpdateCompressionBinariesDatabaseRunnable.kt
@@ -22,14 +22,16 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
+import com.kunzisoft.keepass.hardware.HardwareKey
class UpdateCompressionBinariesDatabaseRunnable (
context: Context,
database: Database,
private val oldCompressionAlgorithm: CompressionAlgorithm,
private val newCompressionAlgorithm: CompressionAlgorithm,
- saveDatabase: Boolean)
- : SaveDatabaseRunnable(context, database, saveDatabase) {
+ saveDatabase: Boolean,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : SaveDatabaseRunnable(context, database, saveDatabase, null, challengeResponseRetriever) {
override fun onStartRun() {
// Set new compression
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/history/DeleteEntryHistoryDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/history/DeleteEntryHistoryDatabaseRunnable.kt
index 2e6c2eab0..58a1ad6ad 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/history/DeleteEntryHistoryDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/history/DeleteEntryHistoryDatabaseRunnable.kt
@@ -23,14 +23,16 @@ import android.content.Context
import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
+import com.kunzisoft.keepass.hardware.HardwareKey
class DeleteEntryHistoryDatabaseRunnable (
context: Context,
database: Database,
private val mainEntry: Entry,
private val entryHistoryPosition: Int,
- saveDatabase: Boolean)
- : SaveDatabaseRunnable(context, database, saveDatabase) {
+ saveDatabase: Boolean,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : SaveDatabaseRunnable(context, database, saveDatabase, null, challengeResponseRetriever) {
override fun onStartRun() {
try {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/history/RestoreEntryHistoryDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/history/RestoreEntryHistoryDatabaseRunnable.kt
index 7163cdf38..6b97d8217 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/history/RestoreEntryHistoryDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/history/RestoreEntryHistoryDatabaseRunnable.kt
@@ -23,6 +23,7 @@ import android.content.Context
import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.tasks.ActionRunnable
class RestoreEntryHistoryDatabaseRunnable (
@@ -30,7 +31,8 @@ class RestoreEntryHistoryDatabaseRunnable (
private val database: Database,
private val mainEntry: Entry,
private val entryHistoryPosition: Int,
- private val saveDatabase: Boolean)
+ private val saveDatabase: Boolean,
+ private val challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
: ActionRunnable() {
private var updateEntryRunnable: UpdateEntryRunnable? = null
@@ -43,12 +45,15 @@ class RestoreEntryHistoryDatabaseRunnable (
historyToRestore.addEntryToHistory(it)
}
// Update the entry with the fresh formatted entry to restore
- updateEntryRunnable = UpdateEntryRunnable(context,
- database,
- mainEntry,
- historyToRestore,
- saveDatabase,
- null)
+ updateEntryRunnable = UpdateEntryRunnable(
+ context,
+ database,
+ mainEntry,
+ historyToRestore,
+ saveDatabase,
+ null,
+ challengeResponseRetriever
+ )
updateEntryRunnable?.onStartRun()
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt
index 2feeb82fc..717c62b6e 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/ActionNodeDatabaseRunnable.kt
@@ -22,13 +22,15 @@ package com.kunzisoft.keepass.database.action.node
import android.content.Context
import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.hardware.HardwareKey
abstract class ActionNodeDatabaseRunnable(
context: Context,
database: Database,
private val afterActionNodesFinish: AfterActionNodesFinish?,
- save: Boolean)
- : SaveDatabaseRunnable(context, database, save) {
+ save: Boolean,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : SaveDatabaseRunnable(context, database, save, null, challengeResponseRetriever) {
/**
* Function do to a node action
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt
index ecfd392c6..1178051cc 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddEntryRunnable.kt
@@ -24,6 +24,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.node.Node
+import com.kunzisoft.keepass.hardware.HardwareKey
class AddEntryRunnable constructor(
context: Context,
@@ -31,8 +32,9 @@ class AddEntryRunnable constructor(
private val mNewEntry: Entry,
private val mParent: Group,
save: Boolean,
- afterActionNodesFinish: AfterActionNodesFinish?)
- : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
+ afterActionNodesFinish: AfterActionNodesFinish?,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save, challengeResponseRetriever) {
override fun nodeAction() {
mNewEntry.touch(modified = true, touchParents = true)
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt
index 5c3285e4c..12607f9eb 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/AddGroupRunnable.kt
@@ -23,6 +23,7 @@ import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.node.Node
+import com.kunzisoft.keepass.hardware.HardwareKey
class AddGroupRunnable constructor(
context: Context,
@@ -30,8 +31,9 @@ class AddGroupRunnable constructor(
private val mNewGroup: Group,
private val mParent: Group,
save: Boolean,
- afterActionNodesFinish: AfterActionNodesFinish?)
- : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
+ afterActionNodesFinish: AfterActionNodesFinish?,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save, challengeResponseRetriever) {
override fun nodeAction() {
mNewGroup.touch(modified = true, touchParents = true)
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyNodesRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyNodesRunnable.kt
index dbdaba94e..792643a67 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyNodesRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/CopyNodesRunnable.kt
@@ -21,11 +21,14 @@ package com.kunzisoft.keepass.database.action.node
import android.content.Context
import android.util.Log
-import com.kunzisoft.keepass.database.element.*
+import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.database.element.Entry
+import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.database.exception.CopyEntryDatabaseException
import com.kunzisoft.keepass.database.exception.CopyGroupDatabaseException
+import com.kunzisoft.keepass.hardware.HardwareKey
class CopyNodesRunnable constructor(
context: Context,
@@ -33,8 +36,9 @@ class CopyNodesRunnable constructor(
private val mNodesToCopy: List,
private val mNewParent: Group,
save: Boolean,
- afterActionNodesFinish: AfterActionNodesFinish?)
- : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
+ afterActionNodesFinish: AfterActionNodesFinish?,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save, challengeResponseRetriever) {
private var mEntriesCopied = ArrayList()
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt
index 2175a6b08..0aca84eac 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/DeleteNodesRunnable.kt
@@ -20,16 +20,20 @@
package com.kunzisoft.keepass.database.action.node
import android.content.Context
-import com.kunzisoft.keepass.database.element.*
+import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.database.element.Entry
+import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.Type
+import com.kunzisoft.keepass.hardware.HardwareKey
class DeleteNodesRunnable(context: Context,
database: Database,
private val mNodesToDelete: List,
save: Boolean,
- afterActionNodesFinish: AfterActionNodesFinish)
- : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
+ afterActionNodesFinish: AfterActionNodesFinish,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save, challengeResponseRetriever) {
private var mOldParent: Group? = null
private var mCanRecycle: Boolean = false
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveNodesRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveNodesRunnable.kt
index 17ad24fa3..2e2991e15 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveNodesRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/MoveNodesRunnable.kt
@@ -21,11 +21,14 @@ package com.kunzisoft.keepass.database.action.node
import android.content.Context
import android.util.Log
-import com.kunzisoft.keepass.database.element.*
+import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.database.element.Entry
+import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.database.exception.MoveEntryDatabaseException
import com.kunzisoft.keepass.database.exception.MoveGroupDatabaseException
+import com.kunzisoft.keepass.hardware.HardwareKey
class MoveNodesRunnable constructor(
context: Context,
@@ -33,8 +36,9 @@ class MoveNodesRunnable constructor(
private val mNodesToMove: List,
private val mNewParent: Group,
save: Boolean,
- afterActionNodesFinish: AfterActionNodesFinish?)
- : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
+ afterActionNodesFinish: AfterActionNodesFinish?,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save, challengeResponseRetriever) {
private var mOldParent: Group? = null
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt
index c1e781248..41f7bafe4 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateEntryRunnable.kt
@@ -24,6 +24,7 @@ import com.kunzisoft.keepass.database.element.Attachment
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.node.Node
+import com.kunzisoft.keepass.hardware.HardwareKey
class UpdateEntryRunnable constructor(
context: Context,
@@ -31,8 +32,9 @@ class UpdateEntryRunnable constructor(
private val mOldEntry: Entry,
private val mNewEntry: Entry,
save: Boolean,
- afterActionNodesFinish: AfterActionNodesFinish?)
- : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
+ afterActionNodesFinish: AfterActionNodesFinish?,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save, challengeResponseRetriever) {
override fun nodeAction() {
if (mOldEntry.nodeId == mNewEntry.nodeId) {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt
index 79e5f635b..53de6a6aa 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/action/node/UpdateGroupRunnable.kt
@@ -23,6 +23,7 @@ import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.node.Node
+import com.kunzisoft.keepass.hardware.HardwareKey
class UpdateGroupRunnable constructor(
context: Context,
@@ -30,8 +31,9 @@ class UpdateGroupRunnable constructor(
private val mOldGroup: Group,
private val mNewGroup: Group,
save: Boolean,
- afterActionNodesFinish: AfterActionNodesFinish?)
- : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save) {
+ afterActionNodesFinish: AfterActionNodesFinish?,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
+ : ActionNodeDatabaseRunnable(context, database, afterActionNodesFinish, save, challengeResponseRetriever) {
override fun nodeAction() {
if (mOldGroup.nodeId == mNewGroup.nodeId) {
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/CompositeKey.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/CompositeKey.kt
new file mode 100644
index 000000000..6423068f9
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/CompositeKey.kt
@@ -0,0 +1,34 @@
+package com.kunzisoft.keepass.database.element
+
+import com.kunzisoft.keepass.hardware.HardwareKey
+
+data class CompositeKey(var passwordData: ByteArray? = null,
+ var keyFileData: ByteArray? = null,
+ var hardwareKey: HardwareKey? = null) {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as CompositeKey
+
+ if (passwordData != null) {
+ if (other.passwordData == null) return false
+ if (!passwordData.contentEquals(other.passwordData)) return false
+ } else if (other.passwordData != null) return false
+ if (keyFileData != null) {
+ if (other.keyFileData == null) return false
+ if (!keyFileData.contentEquals(other.keyFileData)) return false
+ } else if (other.keyFileData != null) return false
+ if (hardwareKey != other.hardwareKey) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = passwordData?.contentHashCode() ?: 0
+ result = 31 * result + (keyFileData?.contentHashCode() ?: 0)
+ result = 31 * result + (hardwareKey?.hashCode() ?: 0)
+ return result
+ }
+}
\ No newline at end of file
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 189a14dbf..a2d594d8d 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
@@ -54,12 +54,10 @@ import com.kunzisoft.keepass.database.file.output.DatabaseOutputKDBX
import com.kunzisoft.keepass.database.merge.DatabaseKDBXMerger
import com.kunzisoft.keepass.database.search.SearchHelper
import com.kunzisoft.keepass.database.search.SearchParameters
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.icons.IconDrawableFactory
-import com.kunzisoft.keepass.model.MainCredential
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
-import com.kunzisoft.keepass.utils.SingletonHolder
-import com.kunzisoft.keepass.utils.UriUtil
-import com.kunzisoft.keepass.utils.readBytes4ToUInt
+import com.kunzisoft.keepass.utils.*
import java.io.*
import java.util.*
@@ -73,7 +71,7 @@ class Database {
var fileUri: Uri? = null
private set
- private var mSearchHelper: SearchHelper? = null
+ private var mSearchHelper: SearchHelper = SearchHelper()
var isReadOnly = false
@@ -384,10 +382,14 @@ class Database {
set(masterKey) {
mDatabaseKDB?.masterKey = masterKey
mDatabaseKDBX?.masterKey = masterKey
+ mDatabaseKDBX?.keyLastChanged = DateInstant()
mDatabaseKDBX?.settingsChanged = DateInstant()
dataModifiedSinceLastLoading = true
}
+ val transformSeed: ByteArray?
+ get() = mDatabaseKDB?.transformSeed ?: mDatabaseKDBX?.transformSeed
+
var rootGroup: Group?
get() {
mDatabaseKDB?.rootGroup?.let {
@@ -553,83 +555,31 @@ class Database {
setDatabaseKDBX(newDatabase)
this.fileUri = databaseUri
// Set Database state
- this.loaded = true
this.dataModifiedSinceLastLoading = false
}
- @Throws(LoadDatabaseException::class)
- private fun readDatabaseStream(contentResolver: ContentResolver, uri: Uri,
- openDatabaseKDB: (InputStream) -> DatabaseKDB,
- openDatabaseKDBX: (InputStream) -> DatabaseKDBX) {
- var databaseInputStream: InputStream? = null
- try {
- // Load Data, pass Uris as InputStreams
- val databaseStream = UriUtil.getUriInputStream(contentResolver, uri)
- ?: throw IOException("Database input stream cannot be retrieve")
-
- databaseInputStream = BufferedInputStream(databaseStream)
- if (!databaseInputStream.markSupported()) {
- throw IOException("Input stream does not support mark.")
- }
-
- // We'll end up reading 8 bytes to identify the header. Might as well use two extra.
- databaseInputStream.mark(10)
-
- // Get the file directory to save the attachments
- val sig1 = databaseInputStream.readBytes4ToUInt()
- val sig2 = databaseInputStream.readBytes4ToUInt()
-
- // Return to the start
- databaseInputStream.reset()
-
- when {
- // Header of database KDB
- DatabaseHeaderKDB.matchesHeader(sig1, sig2) -> setDatabaseKDB(openDatabaseKDB(databaseInputStream))
-
- // Header of database KDBX
- DatabaseHeaderKDBX.matchesHeader(sig1, sig2) -> setDatabaseKDBX(openDatabaseKDBX(databaseInputStream))
-
- // Header not recognized
- else -> throw SignatureDatabaseException()
- }
-
- this.mSearchHelper = SearchHelper()
- loaded = true
- } catch (e: LoadDatabaseException) {
- throw e
- } catch (e: Exception) {
- throw LoadDatabaseException(e)
- } finally {
- databaseInputStream?.close()
- }
- }
-
- @Throws(LoadDatabaseException::class)
- fun loadData(uri: Uri,
- mainCredential: MainCredential,
- readOnly: Boolean,
- contentResolver: ContentResolver,
- cacheDirectory: File,
- isRAMSufficient: (memoryWanted: Long) -> Boolean,
- fixDuplicateUUID: Boolean,
- progressTaskUpdater: ProgressTaskUpdater?) {
+ @Throws(DatabaseInputException::class)
+ fun loadData(
+ contentResolver: ContentResolver,
+ databaseUri: Uri,
+ mainCredential: MainCredential,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
+ readOnly: Boolean,
+ cacheDirectory: File,
+ isRAMSufficient: (memoryWanted: Long) -> Boolean,
+ fixDuplicateUUID: Boolean,
+ progressTaskUpdater: ProgressTaskUpdater?
+ ) {
// Save database URI
- this.fileUri = uri
+ this.fileUri = databaseUri
// Check if the file is writable
this.isReadOnly = readOnly
- // Pass KeyFile Uri as InputStreams
- var keyFileInputStream: InputStream? = null
try {
- // Get keyFile inputStream
- mainCredential.keyFileUri?.let { keyFile ->
- keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyFile)
- }
-
// Read database stream for the first time
- readDatabaseStream(contentResolver, uri,
+ readDatabaseStream(contentResolver, databaseUri,
{ databaseInputStream ->
val databaseKDB = DatabaseKDB().apply {
binaryCache.cacheDirectory = cacheDirectory
@@ -639,12 +589,12 @@ class Database {
.openDatabase(databaseInputStream,
progressTaskUpdater
) {
- databaseKDB.retrieveMasterKey(
- mainCredential.masterPassword,
- keyFileInputStream
+ databaseKDB.deriveMasterKey(
+ contentResolver,
+ mainCredential
)
}
- databaseKDB
+ setDatabaseKDB(databaseKDB)
},
{ databaseInputStream ->
val databaseKDBX = DatabaseKDBX().apply {
@@ -655,23 +605,23 @@ class Database {
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
openDatabase(databaseInputStream,
progressTaskUpdater) {
- databaseKDBX.retrieveMasterKey(
- mainCredential.masterPassword,
- keyFileInputStream,
+ databaseKDBX.deriveMasterKey(
+ contentResolver,
+ mainCredential,
+ challengeResponseRetriever
)
}
}
- databaseKDBX
+ setDatabaseKDBX(databaseKDBX)
}
)
- } catch (e: FileNotFoundException) {
- throw FileNotFoundDatabaseException("Unable to load the keyfile")
- } catch (e: LoadDatabaseException) {
- throw e
+ loaded = true
} catch (e: Exception) {
- throw LoadDatabaseException(e)
+ Log.e(TAG, "Unable to load the database")
+ if (e is DatabaseInputException)
+ throw e
+ throw DatabaseInputException(e)
} finally {
- keyFileInputStream?.close()
dataModifiedSinceLastLoading = false
}
}
@@ -680,48 +630,44 @@ class Database {
return mDatabaseKDBX != null
}
- @Throws(LoadDatabaseException::class)
- fun mergeData(databaseToMergeUri: Uri?,
- databaseToMergeMainCredential: MainCredential?,
- contentResolver: ContentResolver,
- isRAMSufficient: (memoryWanted: Long) -> Boolean,
- progressTaskUpdater: ProgressTaskUpdater?) {
+ @Throws(DatabaseInputException::class)
+ fun mergeData(
+ contentResolver: ContentResolver,
+ databaseToMergeUri: Uri?,
+ databaseToMergeMainCredential: MainCredential?,
+ databaseToMergeChallengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
+ isRAMSufficient: (memoryWanted: Long) -> Boolean,
+ progressTaskUpdater: ProgressTaskUpdater?
+ ) {
mDatabaseKDB?.let {
- throw IODatabaseException("Unable to merge from a database V1")
+ throw MergeDatabaseKDBException()
}
// New database instance to get new changes
val databaseToMerge = Database()
databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri
- // Pass KeyFile Uri as InputStreams
- var keyFileInputStream: InputStream? = null
try {
val databaseUri = databaseToMerge.fileUri
if (databaseUri != null) {
- if (databaseToMergeMainCredential != null) {
- // Get keyFile inputStream
- databaseToMergeMainCredential.keyFileUri?.let { keyFile ->
- keyFileInputStream = UriUtil.getUriInputStream(contentResolver, keyFile)
- }
- }
-
- databaseToMerge.readDatabaseStream(contentResolver, databaseUri,
+ readDatabaseStream(contentResolver, databaseUri,
{ databaseInputStream ->
val databaseToMergeKDB = DatabaseKDB()
DatabaseInputKDB(databaseToMergeKDB)
.openDatabase(databaseInputStream, progressTaskUpdater) {
if (databaseToMergeMainCredential != null) {
- databaseToMergeKDB.retrieveMasterKey(
- databaseToMergeMainCredential.masterPassword,
- keyFileInputStream,
+ databaseToMergeKDB.deriveMasterKey(
+ contentResolver,
+ databaseToMergeMainCredential
)
} else {
- databaseToMergeKDB.masterKey = masterKey
+ this@Database.mDatabaseKDB?.let { thisDatabaseKDB ->
+ databaseToMergeKDB.copyMasterKeyFrom(thisDatabaseKDB)
+ }
}
}
- databaseToMergeKDB
+ databaseToMerge.setDatabaseKDB(databaseToMergeKDB)
},
{ databaseInputStream ->
val databaseToMergeKDBX = DatabaseKDBX()
@@ -729,18 +675,22 @@ class Database {
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
openDatabase(databaseInputStream, progressTaskUpdater) {
if (databaseToMergeMainCredential != null) {
- databaseToMergeKDBX.retrieveMasterKey(
- databaseToMergeMainCredential.masterPassword,
- keyFileInputStream,
+ databaseToMergeKDBX.deriveMasterKey(
+ contentResolver,
+ databaseToMergeMainCredential,
+ databaseToMergeChallengeResponseRetriever
)
} else {
- databaseToMergeKDBX.masterKey = masterKey
+ this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX ->
+ databaseToMergeKDBX.copyMasterKeyFrom(thisDatabaseKDBX)
+ }
}
}
}
- databaseToMergeKDBX
+ databaseToMerge.setDatabaseKDBX(databaseToMergeKDBX)
}
)
+ loaded = true
mDatabaseKDBX?.let { currentDatabaseKDBX ->
val databaseMerger = DatabaseKDBXMerger(currentDatabaseKDBX).apply {
@@ -760,24 +710,24 @@ class Database {
}
}
} else {
- throw IODatabaseException("Database URI is null, database cannot be merged")
+ throw UnknownDatabaseLocationException()
}
- } catch (e: FileNotFoundException) {
- throw FileNotFoundDatabaseException("Unable to load the keyfile")
- } catch (e: LoadDatabaseException) {
- throw e
} catch (e: Exception) {
- throw LoadDatabaseException(e)
+ Log.e(TAG, "Unable to merge the database")
+ if (e is DatabaseException)
+ throw e
+ throw DatabaseInputException(e)
} finally {
- keyFileInputStream?.close()
databaseToMerge.clearAndClose()
}
}
- @Throws(LoadDatabaseException::class)
- fun reloadData(contentResolver: ContentResolver,
- isRAMSufficient: (memoryWanted: Long) -> Boolean,
- progressTaskUpdater: ProgressTaskUpdater?) {
+ @Throws(DatabaseInputException::class)
+ fun reloadData(
+ contentResolver: ContentResolver,
+ isRAMSufficient: (memoryWanted: Long) -> Boolean,
+ progressTaskUpdater: ProgressTaskUpdater?
+ ) {
// Retrieve the stream from the old database URI
try {
@@ -791,9 +741,11 @@ class Database {
}
DatabaseInputKDB(databaseKDB)
.openDatabase(databaseInputStream, progressTaskUpdater) {
- databaseKDB.masterKey = masterKey
+ this@Database.mDatabaseKDB?.let { thisDatabaseKDB ->
+ databaseKDB.copyMasterKeyFrom(thisDatabaseKDB)
+ }
}
- databaseKDB
+ setDatabaseKDB(databaseKDB)
},
{ databaseInputStream ->
val databaseKDBX = DatabaseKDBX()
@@ -803,26 +755,144 @@ class Database {
DatabaseInputKDBX(databaseKDBX).apply {
setMethodToCheckIfRAMIsSufficient(isRAMSufficient)
openDatabase(databaseInputStream, progressTaskUpdater) {
- databaseKDBX.masterKey = masterKey
+ this@Database.mDatabaseKDBX?.let { thisDatabaseKDBX ->
+ databaseKDBX.copyMasterKeyFrom(thisDatabaseKDBX)
+ }
}
}
- databaseKDBX
+ setDatabaseKDBX(databaseKDBX)
}
)
+ loaded = true
} else {
- throw IODatabaseException("Database URI is null, database cannot be reloaded")
+ throw UnknownDatabaseLocationException()
}
- } catch (e: FileNotFoundException) {
- throw FileNotFoundDatabaseException("Unable to load the keyfile")
- } catch (e: LoadDatabaseException) {
- throw e
} catch (e: Exception) {
- throw LoadDatabaseException(e)
+ Log.e(TAG, "Unable to reload the database")
+ if (e is DatabaseException)
+ throw e
+ throw DatabaseInputException(e)
} finally {
dataModifiedSinceLastLoading = false
}
}
+ @Throws(Exception::class)
+ private fun readDatabaseStream(contentResolver: ContentResolver,
+ databaseUri: Uri,
+ openDatabaseKDB: (InputStream) -> Unit,
+ openDatabaseKDBX: (InputStream) -> Unit) {
+ try {
+ // Load Data, pass Uris as InputStreams
+ val databaseStream = UriUtil.getUriInputStream(contentResolver, databaseUri)
+ ?: throw UnknownDatabaseLocationException()
+
+ BufferedInputStream(databaseStream).use { databaseInputStream ->
+
+ // We'll end up reading 8 bytes to identify the header. Might as well use two extra.
+ databaseInputStream.mark(10)
+
+ // Get the file directory to save the attachments
+ val sig1 = databaseInputStream.readBytes4ToUInt()
+ val sig2 = databaseInputStream.readBytes4ToUInt()
+
+ // Return to the start
+ databaseInputStream.reset()
+
+ when {
+ // Header of database KDB
+ DatabaseHeaderKDB.matchesHeader(sig1, sig2) -> openDatabaseKDB(
+ databaseInputStream
+ )
+ // Header of database KDBX
+ DatabaseHeaderKDBX.matchesHeader(sig1, sig2) -> openDatabaseKDBX(
+ databaseInputStream
+ )
+ // Header not recognized
+ else -> throw SignatureDatabaseException()
+ }
+ }
+ } catch (fileNotFoundException : FileNotFoundException) {
+ throw FileNotFoundDatabaseException()
+ }
+ }
+
+ @Throws(DatabaseOutputException::class)
+ fun saveData(contentResolver: ContentResolver,
+ cacheDir: File,
+ databaseCopyUri: Uri?,
+ mainCredential: MainCredential?,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray) {
+ val saveUri = databaseCopyUri ?: this.fileUri
+ // Build temp database file to avoid file corruption if error
+ val cacheFile = File(cacheDir, saveUri.hashCode().toString())
+ try {
+ if (saveUri != null) {
+ // Save in a temp memory to avoid exception
+ cacheFile.outputStream().use { outputStream ->
+ mDatabaseKDB?.let { databaseKDB ->
+ DatabaseOutputKDB(databaseKDB).apply {
+ writeDatabase(outputStream) {
+ if (mainCredential != null) {
+ databaseKDB.deriveMasterKey(
+ contentResolver,
+ mainCredential
+ )
+ } else {
+ // No master key change
+ }
+ }
+ }
+ }
+ ?: mDatabaseKDBX?.let { databaseKDBX ->
+ DatabaseOutputKDBX(databaseKDBX).apply {
+ writeDatabase(outputStream) {
+ if (mainCredential != null) {
+ // Build new master key from MainCredential
+ databaseKDBX.deriveMasterKey(
+ contentResolver,
+ mainCredential,
+ challengeResponseRetriever
+ )
+ } else {
+ // Reuse composite key parts
+ databaseKDBX.deriveCompositeKey(
+ challengeResponseRetriever
+ )
+ }
+ }
+ }
+ }
+ }
+ // Copy from the cache to the final stream
+ UriUtil.getUriOutputStream(contentResolver, saveUri)?.use { outputStream ->
+ cacheFile.inputStream().use { inputStream ->
+ inputStream.readAllBytes { buffer ->
+ outputStream.write(buffer)
+ }
+ }
+ }
+ } else {
+ throw UnknownDatabaseLocationException()
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to save database", e)
+ if (e is DatabaseException)
+ throw e
+ throw DatabaseOutputException(e)
+ } finally {
+ try {
+ Log.d(TAG, "Delete database cache file $cacheFile")
+ cacheFile.delete()
+ } catch (e: Exception) {
+ Log.e(TAG, "Cache file $cacheFile cannot be deleted", e)
+ }
+ if (databaseCopyUri == null) {
+ this.dataModifiedSinceLastLoading = false
+ }
+ }
+ }
+
fun groupIsInRecycleBin(group: Group): Boolean {
val groupKDB = group.groupKDB
val groupKDBX = group.groupKDBX
@@ -845,13 +915,13 @@ class Database {
fun createVirtualGroupFromSearch(searchParameters: SearchParameters,
fromGroup: NodeId<*>? = null,
max: Int = Integer.MAX_VALUE): Group? {
- return mSearchHelper?.createVirtualGroupWithSearchResult(this,
+ return mSearchHelper.createVirtualGroupWithSearchResult(this,
searchParameters, fromGroup, max)
}
fun createVirtualGroupFromSearchInfo(searchInfoString: String,
max: Int = Integer.MAX_VALUE): Group? {
- return mSearchHelper?.createVirtualGroupWithSearchResult(this,
+ return mSearchHelper.createVirtualGroupWithSearchResult(this,
SearchParameters().apply {
searchQuery = searchInfoString
searchInTitles = true
@@ -908,40 +978,6 @@ class Database {
dataModifiedSinceLastLoading = true
}
- @Throws(DatabaseOutputException::class)
- fun saveData(databaseCopyUri: Uri?, contentResolver: ContentResolver) {
- try {
- val saveUri = databaseCopyUri ?: this.fileUri
- if (saveUri != null) {
- var outputStream: OutputStream? = null
- try {
- outputStream = UriUtil.getUriOutputStream(contentResolver, saveUri)
- outputStream?.let { definedOutputStream ->
- val databaseOutput =
- mDatabaseKDB?.let { DatabaseOutputKDB(it, definedOutputStream) }
- ?: mDatabaseKDBX?.let {
- DatabaseOutputKDBX(
- it,
- definedOutputStream
- )
- }
- databaseOutput?.output()
- }
- } catch (e: Exception) {
- throw IOException(e)
- } finally {
- outputStream?.close()
- }
- if (databaseCopyUri == null) {
- this.dataModifiedSinceLastLoading = false
- }
- }
- } catch (e: Exception) {
- Log.e(TAG, "Unable to save database", e)
- throw DatabaseOutputException(e)
- }
- }
-
fun clearIndexesAndBinaries(filesDirectory: File? = null) {
this.mDatabaseKDB?.clearIndexes()
this.mDatabaseKDBX?.clearIndexes()
@@ -987,20 +1023,13 @@ class Database {
}
fun validatePasswordEncoding(mainCredential: MainCredential): Boolean {
- val password = mainCredential.masterPassword
+ val password = mainCredential.password
val containsKeyFile = mainCredential.keyFileUri != null
return mDatabaseKDB?.validatePasswordEncoding(password, containsKeyFile)
?: mDatabaseKDBX?.validatePasswordEncoding(password, containsKeyFile)
?: false
}
- @Throws(IOException::class)
- fun assignMasterKey(key: String?, keyInputStream: InputStream?) {
- mDatabaseKDB?.retrieveMasterKey(key, keyInputStream)
- mDatabaseKDBX?.retrieveMasterKey(key, keyInputStream)
- mDatabaseKDBX?.keyLastChanged = DateInstant()
- }
-
fun rootCanContainsEntry(): Boolean {
return mDatabaseKDB?.rootCanContainsEntry() ?: mDatabaseKDBX?.rootCanContainsEntry() ?: false
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt
new file mode 100644
index 000000000..172c228ec
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/MainCredential.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2022 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePassDX.
+ *
+ * KeePassDX 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.
+ *
+ * KeePassDX 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 KeePassDX. If not, see .
+ */
+package com.kunzisoft.keepass.database.element
+
+import android.content.ContentResolver
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import android.util.Base64
+import android.util.Log
+import com.kunzisoft.encrypt.HashManager
+import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
+import com.kunzisoft.keepass.hardware.HardwareKey
+import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
+import com.kunzisoft.keepass.utils.StringUtil.toHexString
+import com.kunzisoft.keepass.utils.UriUtil
+import com.kunzisoft.keepass.utils.readEnum
+import com.kunzisoft.keepass.utils.writeEnum
+import org.apache.commons.codec.binary.Hex
+import org.w3c.dom.Node
+import java.io.ByteArrayInputStream
+import java.io.IOException
+import java.io.InputStream
+import java.io.UnsupportedEncodingException
+import java.nio.charset.Charset
+import javax.xml.XMLConstants
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.parsers.ParserConfigurationException
+
+data class MainCredential(var password: String? = null,
+ var keyFileUri: Uri? = null,
+ var hardwareKey: HardwareKey? = null): Parcelable {
+
+ constructor(parcel: Parcel) : this() {
+ password = parcel.readString()
+ keyFileUri = parcel.readParcelable(Uri::class.java.classLoader)
+ hardwareKey = parcel.readEnum()
+ }
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeString(password)
+ parcel.writeParcelable(keyFileUri, flags)
+ parcel.writeEnum(hardwareKey)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as MainCredential
+
+ if (password != other.password) return false
+ if (keyFileUri != other.keyFileUri) return false
+ if (hardwareKey != other.hardwareKey) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = password?.hashCode() ?: 0
+ result = 31 * result + (keyFileUri?.hashCode() ?: 0)
+ result = 31 * result + (hardwareKey?.hashCode() ?: 0)
+ return result
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): MainCredential {
+ return MainCredential(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+
+ private val TAG = MainCredential::class.java.simpleName
+
+ @Throws(IOException::class)
+ fun retrievePasswordKey(key: String,
+ encoding: Charset
+ ): ByteArray {
+ val bKey: ByteArray = try {
+ key.toByteArray(encoding)
+ } catch (e: UnsupportedEncodingException) {
+ key.toByteArray()
+ }
+ return HashManager.hashSha256(bKey)
+ }
+
+ @Throws(IOException::class)
+ fun retrieveFileKey(contentResolver: ContentResolver,
+ keyFileUri: Uri?,
+ allowXML: Boolean): ByteArray {
+ if (keyFileUri == null)
+ throw IOException("Keyfile URI is null")
+ val keyData = getKeyFileData(contentResolver, keyFileUri)
+ ?: throw IOException("No data retrieved")
+ try {
+ // Check XML key file
+ val xmlKeyByteArray = if (allowXML)
+ loadXmlKeyFile(ByteArrayInputStream(keyData))
+ else
+ null
+ if (xmlKeyByteArray != null) {
+ return xmlKeyByteArray
+ }
+
+ // Check 32 bytes key file
+ when (keyData.size) {
+ 32 -> return keyData
+ 64 -> try {
+ return Hex.decodeHex(String(keyData).toCharArray())
+ } catch (ignoredException: Exception) {
+ // Key is not base 64, treat it as binary data
+ }
+ }
+ // Hash file as binary data
+ return HashManager.hashSha256(keyData)
+ } catch (e: Exception) {
+ throw IOException("Unable to load the keyfile.", e)
+ }
+ }
+
+ @Throws(IOException::class)
+ fun retrieveHardwareKey(keyData: ByteArray): ByteArray {
+ return HashManager.hashSha256(keyData)
+ }
+
+ @Throws(Exception::class)
+ private fun getKeyFileData(contentResolver: ContentResolver,
+ keyFileUri: Uri): ByteArray? {
+ UriUtil.getUriInputStream(contentResolver, keyFileUri)?.use { keyFileInputStream ->
+ return keyFileInputStream.readBytes()
+ }
+ return null
+ }
+
+ private fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? {
+ try {
+ val documentBuilderFactory = DocumentBuilderFactory.newInstance()
+
+ // Disable certain unsecure XML-Parsing DocumentBuilderFactory features
+ try {
+ documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
+ } catch (e : ParserConfigurationException) {
+ Log.w(TAG, "Unable to add FEATURE_SECURE_PROCESSING to prevent XML eXternal Entity injection (XXE)")
+ }
+
+ val documentBuilder = documentBuilderFactory.newDocumentBuilder()
+ val doc = documentBuilder.parse(keyInputStream)
+
+ var xmlKeyFileVersion = 1F
+
+ val docElement = doc.documentElement
+ val keyFileChildNodes = docElement.childNodes
+ // Root node
+ if (docElement == null
+ || !docElement.nodeName.equals(XML_NODE_ROOT_NAME, ignoreCase = true)) {
+ return null
+ }
+ if (keyFileChildNodes.length < 2)
+ return null
+ for (keyFileChildPosition in 0 until keyFileChildNodes.length) {
+ val keyFileChildNode = keyFileChildNodes.item(keyFileChildPosition)
+ //
+ if (keyFileChildNode.nodeName.equals(XML_NODE_META_NAME, ignoreCase = true)) {
+ val metaChildNodes = keyFileChildNode.childNodes
+ for (metaChildPosition in 0 until metaChildNodes.length) {
+ val metaChildNode = metaChildNodes.item(metaChildPosition)
+ //
+ if (metaChildNode.nodeName.equals(XML_NODE_VERSION_NAME, ignoreCase = true)) {
+ val versionChildNodes = metaChildNode.childNodes
+ for (versionChildPosition in 0 until versionChildNodes.length) {
+ val versionChildNode = versionChildNodes.item(versionChildPosition)
+ if (versionChildNode.nodeType == Node.TEXT_NODE) {
+ val versionText = versionChildNode.textContent.removeSpaceChars()
+ try {
+ xmlKeyFileVersion = versionText.toFloat()
+ Log.i(TAG, "Reading XML KeyFile version : $xmlKeyFileVersion")
+ } catch (e: Exception) {
+ Log.e(TAG, "XML Keyfile version cannot be read : $versionText")
+ }
+ }
+ }
+ }
+ }
+ }
+ //
+ if (keyFileChildNode.nodeName.equals(XML_NODE_KEY_NAME, ignoreCase = true)) {
+ val keyChildNodes = keyFileChildNode.childNodes
+ for (keyChildPosition in 0 until keyChildNodes.length) {
+ val keyChildNode = keyChildNodes.item(keyChildPosition)
+ //
+ if (keyChildNode.nodeName.equals(XML_NODE_DATA_NAME, ignoreCase = true)) {
+ var hashString : String? = null
+ if (keyChildNode.hasAttributes()) {
+ val dataNodeAttributes = keyChildNode.attributes
+ hashString = dataNodeAttributes
+ .getNamedItem(XML_ATTRIBUTE_DATA_HASH).nodeValue
+ }
+ val dataChildNodes = keyChildNode.childNodes
+ for (dataChildPosition in 0 until dataChildNodes.length) {
+ val dataChildNode = dataChildNodes.item(dataChildPosition)
+ if (dataChildNode.nodeType == Node.TEXT_NODE) {
+ val dataString = dataChildNode.textContent.removeSpaceChars()
+ when (xmlKeyFileVersion) {
+ 1F -> {
+ // No hash in KeyFile XML version 1
+ return Base64.decode(dataString,
+ DatabaseKDBX.BASE_64_FLAG
+ )
+ }
+ 2F -> {
+ return if (hashString != null
+ && checkKeyFileHash(dataString, hashString)
+ ) {
+ Log.i(TAG, "Successful key file hash check.")
+ Hex.decodeHex(dataString.toCharArray())
+ } else {
+ Log.e(TAG, "Unable to check the hash of the key file.")
+ null
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (e: Exception) {
+ return null
+ }
+ return null
+ }
+
+ private fun checkKeyFileHash(data: String, hash: String): Boolean {
+ var success = false
+ try {
+ // hexadecimal encoding of the first 4 bytes of the SHA-256 hash of the key.
+ val dataDigest = HashManager.hashSha256(Hex.decodeHex(data.toCharArray()))
+ .copyOfRange(0, 4).toHexString()
+ success = dataDigest == hash
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return success
+ }
+
+ private const val XML_NODE_ROOT_NAME = "KeyFile"
+ private const val XML_NODE_META_NAME = "Meta"
+ private const val XML_NODE_VERSION_NAME = "Version"
+ private const val XML_NODE_KEY_NAME = "Key"
+ private const val XML_NODE_DATA_NAME = "Data"
+ private const val XML_ATTRIBUTE_DATA_HASH = "Hash"
+ }
+}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt
index f10901014..b98826cb3 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt
@@ -19,11 +19,13 @@
package com.kunzisoft.keepass.database.element.database
+import android.content.ContentResolver
import com.kunzisoft.encrypt.HashManager
import com.kunzisoft.encrypt.aes.AESTransformer
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.element.entry.EntryKDB
import com.kunzisoft.keepass.database.element.group.GroupKDB
@@ -31,8 +33,10 @@ import com.kunzisoft.keepass.database.element.icon.IconImageStandard
import com.kunzisoft.keepass.database.element.node.NodeIdInt
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
import com.kunzisoft.keepass.database.element.node.NodeVersioned
+import com.kunzisoft.keepass.database.exception.EmptyKeyDatabaseException
+import com.kunzisoft.keepass.database.exception.HardwareKeyDatabaseException
import java.io.IOException
-import java.io.InputStream
+import java.nio.charset.Charset
import java.util.*
class DatabaseKDB : DatabaseVersioned() {
@@ -56,8 +60,8 @@ class DatabaseKDB : DatabaseVersioned() {
KdfFactory.aesKdf
)
- override val passwordEncoding: String
- get() = "ISO-8859-1"
+ override val passwordEncoding: Charset
+ get() = Charsets.ISO_8859_1
override var numberKeyEncryptionRounds = 300L
@@ -116,20 +120,6 @@ class DatabaseKDB : DatabaseVersioned() {
return newId
}
- @Throws(IOException::class)
- override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
-
- return if (key != null && keyInputStream != null) {
- getCompositeKey(key, keyInputStream)
- } else if (key != null) { // key.length() >= 0
- getPasswordKey(key)
- } else if (keyInputStream != null) { // key == null
- getFileKey(keyInputStream)
- } else {
- throw IllegalArgumentException("Key cannot be empty.")
- }
- }
-
@Throws(IOException::class)
fun makeFinalKey(masterSeed: ByteArray, transformSeed: ByteArray, numRounds: Long) {
// Encrypt the master key a few times to make brute-force key-search harder
@@ -138,6 +128,41 @@ class DatabaseKDB : DatabaseVersioned() {
finalKey = HashManager.hashSha256(masterSeed, transformedKey)
}
+ fun deriveMasterKey(
+ contentResolver: ContentResolver,
+ mainCredential: MainCredential
+ ) {
+ // Exception when no password
+ if (mainCredential.hardwareKey != null)
+ throw HardwareKeyDatabaseException()
+ if (mainCredential.password == null && mainCredential.keyFileUri == null)
+ throw EmptyKeyDatabaseException()
+
+ // Retrieve plain data
+ val password = mainCredential.password
+ val keyFileUri = mainCredential.keyFileUri
+ val passwordBytes = if (password != null) MainCredential.retrievePasswordKey(
+ password,
+ passwordEncoding
+ ) else null
+ val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey(
+ contentResolver,
+ keyFileUri,
+ false
+ ) else null
+
+ // Build master key
+ if (passwordBytes != null
+ && keyFileBytes != null) {
+ this.masterKey = HashManager.hashSha256(
+ passwordBytes,
+ keyFileBytes
+ )
+ } else {
+ this.masterKey = passwordBytes ?: keyFileBytes ?: byteArrayOf(0)
+ }
+ }
+
override fun createGroup(): GroupKDB {
return GroupKDB()
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt
index 6d1602409..5440faf64 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt
@@ -19,6 +19,7 @@
*/
package com.kunzisoft.keepass.database.element.database
+import android.content.ContentResolver
import android.content.res.Resources
import android.util.Base64
import android.util.Log
@@ -31,10 +32,7 @@ import com.kunzisoft.keepass.database.crypto.kdf.AesKdf
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
import com.kunzisoft.keepass.database.crypto.kdf.KdfParameters
-import com.kunzisoft.keepass.database.element.CustomData
-import com.kunzisoft.keepass.database.element.DateInstant
-import com.kunzisoft.keepass.database.element.DeletedObject
-import com.kunzisoft.keepass.database.element.Tags
+import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.element.database.DatabaseKDB.Companion.BACKUP_FOLDER_TITLE
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
@@ -49,30 +47,27 @@ import com.kunzisoft.keepass.database.element.node.NodeVersioned
import com.kunzisoft.keepass.database.element.security.MemoryProtectionConfig
import com.kunzisoft.keepass.database.element.template.Template
import com.kunzisoft.keepass.database.element.template.TemplateEngineCompatible
+import com.kunzisoft.keepass.database.exception.DatabaseOutputException
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_31
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_40
import com.kunzisoft.keepass.database.file.DatabaseHeaderKDBX.Companion.FILE_VERSION_41
-import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
-import com.kunzisoft.keepass.utils.StringUtil.toHexString
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.utils.UnsignedInt
import com.kunzisoft.keepass.utils.longTo8Bytes
-import org.apache.commons.codec.binary.Hex
-import org.w3c.dom.Node
import java.io.IOException
-import java.io.InputStream
+import java.nio.charset.Charset
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
import javax.crypto.Mac
-import javax.xml.XMLConstants
-import javax.xml.parsers.DocumentBuilderFactory
-import javax.xml.parsers.ParserConfigurationException
-import kotlin.collections.HashSet
import kotlin.math.min
class DatabaseKDBX : DatabaseVersioned {
+ // To resave the database with same credential when already loaded
+ private var mCompositeKey = CompositeKey()
+
var hmacKey: ByteArray? = null
private set
@@ -233,6 +228,79 @@ class DatabaseKDBX : DatabaseVersioned {
}
}
+ fun deriveMasterKey(
+ contentResolver: ContentResolver,
+ mainCredential: MainCredential,
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray
+ ) {
+ // Retrieve each plain credential
+ val password = mainCredential.password
+ val keyFileUri = mainCredential.keyFileUri
+ val hardwareKey = mainCredential.hardwareKey
+ val passwordBytes = if (password != null) MainCredential.retrievePasswordKey(
+ password,
+ passwordEncoding
+ ) else null
+ val keyFileBytes = if (keyFileUri != null) MainCredential.retrieveFileKey(
+ contentResolver,
+ keyFileUri,
+ true
+ ) else null
+ val hardwareKeyBytes = if (hardwareKey != null) MainCredential.retrieveHardwareKey(
+ challengeResponseRetriever.invoke(hardwareKey, transformSeed)
+ ) else null
+
+ // Save to rebuild master password with new seed later
+ mCompositeKey = CompositeKey(passwordBytes, keyFileBytes, hardwareKey)
+
+ // Build the master key
+ this.masterKey = composedKeyToMasterKey(
+ passwordBytes,
+ keyFileBytes,
+ hardwareKeyBytes
+ )
+ }
+
+ @Throws(DatabaseOutputException::class)
+ fun deriveCompositeKey(
+ challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray
+ ) {
+ val passwordBytes = mCompositeKey.passwordData
+ val keyFileBytes = mCompositeKey.keyFileData
+ val hardwareKey = mCompositeKey.hardwareKey
+ if (hardwareKey == null) {
+ // If no hardware key, simply rebuild from composed keys
+ this.masterKey = composedKeyToMasterKey(
+ passwordBytes,
+ keyFileBytes
+ )
+ } else {
+ val hardwareKeyBytes = MainCredential.retrieveHardwareKey(
+ challengeResponseRetriever.invoke(hardwareKey, transformSeed)
+ )
+ this.masterKey = composedKeyToMasterKey(
+ passwordBytes,
+ keyFileBytes,
+ hardwareKeyBytes
+ )
+ }
+ }
+
+ private fun composedKeyToMasterKey(passwordData: ByteArray?,
+ keyFileData: ByteArray?,
+ hardwareKeyData: ByteArray? = null): ByteArray {
+ return HashManager.hashSha256(
+ passwordData,
+ keyFileData,
+ hardwareKeyData
+ )
+ }
+
+ fun copyMasterKeyFrom(databaseVersioned: DatabaseKDBX) {
+ super.copyMasterKeyFrom(databaseVersioned)
+ this.mCompositeKey = databaseVersioned.mCompositeKey
+ }
+
fun getMinKdbxVersion(): UnsignedInt {
val entryHandler = EntryOperationHandler()
val groupHandler = GroupOperationHandler()
@@ -364,8 +432,8 @@ class DatabaseKDBX : DatabaseVersioned {
kdfEngine.setParallelism(kdfParameters!!, parallelism)
}
- override val passwordEncoding: String
- get() = "UTF-8"
+ override val passwordEncoding: Charset
+ get() = Charsets.UTF_8
private fun getGroupByUUID(groupUUID: UUID): GroupKDBX? {
if (groupUUID == UUID_ZERO)
@@ -528,22 +596,6 @@ class DatabaseKDBX : DatabaseVersioned {
return mFieldReferenceEngine.compile(textReference, recursionLevel)
}
- @Throws(IOException::class)
- public override fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray {
-
- var masterKey = byteArrayOf()
-
- if (key != null && keyInputStream != null) {
- return getCompositeKey(key, keyInputStream)
- } else if (key != null) { // key.length() >= 0
- masterKey = getPasswordKey(key)
- } else if (keyInputStream != null) { // key == null
- masterKey = getFileKey(keyInputStream)
- }
-
- return HashManager.hashSha256(masterKey)
- }
-
@Throws(IOException::class)
fun makeFinalKey(masterSeed: ByteArray) {
@@ -615,115 +667,6 @@ class DatabaseKDBX : DatabaseVersioned {
return ret
}
- override fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? {
- try {
- val documentBuilderFactory = DocumentBuilderFactory.newInstance()
-
- // Disable certain unsecure XML-Parsing DocumentBuilderFactory features
- try {
- documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
- } catch (e : ParserConfigurationException) {
- Log.w(TAG, "Unable to add FEATURE_SECURE_PROCESSING to prevent XML eXternal Entity injection (XXE)")
- }
-
- val documentBuilder = documentBuilderFactory.newDocumentBuilder()
- val doc = documentBuilder.parse(keyInputStream)
-
- var xmlKeyFileVersion = 1F
-
- val docElement = doc.documentElement
- val keyFileChildNodes = docElement.childNodes
- // Root node
- if (docElement == null
- || !docElement.nodeName.equals(XML_NODE_ROOT_NAME, ignoreCase = true)) {
- return null
- }
- if (keyFileChildNodes.length < 2)
- return null
- for (keyFileChildPosition in 0 until keyFileChildNodes.length) {
- val keyFileChildNode = keyFileChildNodes.item(keyFileChildPosition)
- //
- if (keyFileChildNode.nodeName.equals(XML_NODE_META_NAME, ignoreCase = true)) {
- val metaChildNodes = keyFileChildNode.childNodes
- for (metaChildPosition in 0 until metaChildNodes.length) {
- val metaChildNode = metaChildNodes.item(metaChildPosition)
- //
- if (metaChildNode.nodeName.equals(XML_NODE_VERSION_NAME, ignoreCase = true)) {
- val versionChildNodes = metaChildNode.childNodes
- for (versionChildPosition in 0 until versionChildNodes.length) {
- val versionChildNode = versionChildNodes.item(versionChildPosition)
- if (versionChildNode.nodeType == Node.TEXT_NODE) {
- val versionText = versionChildNode.textContent.removeSpaceChars()
- try {
- xmlKeyFileVersion = versionText.toFloat()
- Log.i(TAG, "Reading XML KeyFile version : $xmlKeyFileVersion")
- } catch (e: Exception) {
- Log.e(TAG, "XML Keyfile version cannot be read : $versionText")
- }
- }
- }
- }
- }
- }
- //
- if (keyFileChildNode.nodeName.equals(XML_NODE_KEY_NAME, ignoreCase = true)) {
- val keyChildNodes = keyFileChildNode.childNodes
- for (keyChildPosition in 0 until keyChildNodes.length) {
- val keyChildNode = keyChildNodes.item(keyChildPosition)
- //
- if (keyChildNode.nodeName.equals(XML_NODE_DATA_NAME, ignoreCase = true)) {
- var hashString : String? = null
- if (keyChildNode.hasAttributes()) {
- val dataNodeAttributes = keyChildNode.attributes
- hashString = dataNodeAttributes
- .getNamedItem(XML_ATTRIBUTE_DATA_HASH).nodeValue
- }
- val dataChildNodes = keyChildNode.childNodes
- for (dataChildPosition in 0 until dataChildNodes.length) {
- val dataChildNode = dataChildNodes.item(dataChildPosition)
- if (dataChildNode.nodeType == Node.TEXT_NODE) {
- val dataString = dataChildNode.textContent.removeSpaceChars()
- when (xmlKeyFileVersion) {
- 1F -> {
- // No hash in KeyFile XML version 1
- return Base64.decode(dataString, BASE_64_FLAG)
- }
- 2F -> {
- return if (hashString != null
- && checkKeyFileHash(dataString, hashString)) {
- Log.i(TAG, "Successful key file hash check.")
- Hex.decodeHex(dataString.toCharArray())
- } else {
- Log.e(TAG, "Unable to check the hash of the key file.")
- null
- }
- }
- }
- }
- }
- }
- }
- }
- }
- } catch (e: Exception) {
- return null
- }
- return null
- }
-
- private fun checkKeyFileHash(data: String, hash: String): Boolean {
- var success = false
- try {
- // hexadecimal encoding of the first 4 bytes of the SHA-256 hash of the key.
- val dataDigest = HashManager.hashSha256(Hex.decodeHex(data.toCharArray()))
- .copyOfRange(0, 4).toHexString()
- success = dataDigest == hash
- } catch (e: Exception) {
- e.printStackTrace()
- }
- return success
- }
-
override fun newGroupId(): NodeIdUUID {
var newId: NodeIdUUID
do {
@@ -928,13 +871,6 @@ class DatabaseKDBX : DatabaseVersioned {
private const val DEFAULT_HISTORY_MAX_ITEMS = 10 // -1 unlimited
private const val DEFAULT_HISTORY_MAX_SIZE = (6 * 1024 * 1024).toLong() // -1 unlimited
- private const val XML_NODE_ROOT_NAME = "KeyFile"
- private const val XML_NODE_META_NAME = "Meta"
- private const val XML_NODE_VERSION_NAME = "Version"
- private const val XML_NODE_KEY_NAME = "Key"
- private const val XML_NODE_DATA_NAME = "Data"
- private const val XML_ATTRIBUTE_DATA_HASH = "Hash"
-
const val BASE_64_FLAG = Base64.NO_WRAP
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt
index 3ffae91b2..31597608f 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseVersioned.kt
@@ -20,7 +20,6 @@
package com.kunzisoft.keepass.database.element.database
import android.util.Log
-import com.kunzisoft.encrypt.HashManager
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.binary.AttachmentPool
@@ -32,11 +31,9 @@ import com.kunzisoft.keepass.database.element.icon.IconsManager
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
-import org.apache.commons.codec.binary.Hex
-import java.io.ByteArrayInputStream
-import java.io.IOException
import java.io.InputStream
import java.io.UnsupportedEncodingException
+import java.nio.charset.Charset
import java.util.*
abstract class DatabaseVersioned<
@@ -46,7 +43,6 @@ abstract class DatabaseVersioned<
Entry : EntryVersioned
> {
-
// Algorithm used to encrypt the database
abstract var encryptionAlgorithm: EncryptionAlgorithm
abstract val availableEncryptionAlgorithms: List
@@ -55,11 +51,12 @@ abstract class DatabaseVersioned<
abstract val kdfAvailableList: List
abstract var numberKeyEncryptionRounds: Long
- protected abstract val passwordEncoding: String
+ abstract val passwordEncoding: Charset
var masterKey = ByteArray(32)
var finalKey: ByteArray? = null
protected set
+ var transformSeed: ByteArray? = null
abstract val version: String
abstract val defaultFileExtension: String
@@ -91,58 +88,6 @@ abstract class DatabaseVersioned<
return getGroupIndexes().filter { it != rootGroup }
}
- @Throws(IOException::class)
- protected abstract fun getMasterKey(key: String?, keyInputStream: InputStream?): ByteArray
-
- @Throws(IOException::class)
- fun retrieveMasterKey(key: String?, keyfileInputStream: InputStream?) {
- masterKey = getMasterKey(key, keyfileInputStream)
- }
-
- @Throws(IOException::class)
- protected fun getCompositeKey(key: String, keyfileInputStream: InputStream): ByteArray {
- val fileKey = getFileKey(keyfileInputStream)
- val passwordKey = getPasswordKey(key)
- return HashManager.hashSha256(passwordKey, fileKey)
- }
-
- @Throws(IOException::class)
- protected fun getPasswordKey(key: String): ByteArray {
- val bKey: ByteArray = try {
- key.toByteArray(charset(passwordEncoding))
- } catch (e: UnsupportedEncodingException) {
- key.toByteArray()
- }
- return HashManager.hashSha256(bKey)
- }
-
- @Throws(IOException::class)
- protected fun getFileKey(keyInputStream: InputStream): ByteArray {
- try {
- val keyData = keyInputStream.readBytes()
-
- // Check XML key file
- val xmlKeyByteArray = loadXmlKeyFile(ByteArrayInputStream(keyData))
- if (xmlKeyByteArray != null) {
- return xmlKeyByteArray
- }
-
- // Check 32 bytes key file
- when (keyData.size) {
- 32 -> return keyData
- 64 -> try {
- return Hex.decodeHex(String(keyData).toCharArray())
- } catch (ignoredException: Exception) {
- // Key is not base 64, treat it as binary data
- }
- }
- // Hash file as binary data
- return HashManager.hashSha256(keyData)
- } catch (outOfMemoryError: OutOfMemoryError) {
- throw IOException("Keyfile data is too large", outOfMemoryError)
- }
- }
-
protected open fun loadXmlKeyFile(keyInputStream: InputStream): ByteArray? {
return null
}
@@ -158,20 +103,25 @@ abstract class DatabaseVersioned<
val bKey: ByteArray
try {
- bKey = password.toByteArray(charset(encoding))
+ bKey = password.toByteArray(encoding)
} catch (e: UnsupportedEncodingException) {
return false
}
val reEncoded: String
try {
- reEncoded = String(bKey, charset(encoding))
+ reEncoded = String(bKey, encoding)
} catch (e: UnsupportedEncodingException) {
return false
}
return password == reEncoded
}
+ fun copyMasterKeyFrom(databaseVersioned: DatabaseVersioned) {
+ this.masterKey = databaseVersioned.masterKey
+ this.transformSeed = databaseVersioned.transformSeed
+ }
+
/*
* -------------------------------------
* Node Creation
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt b/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt
index 07f69ab4e..6cfd2eaaa 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/exception/DatabaseException.kt
@@ -24,128 +24,172 @@ import androidx.annotation.StringRes
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
+import java.io.PrintStream
+import java.io.PrintWriter
abstract class DatabaseException : Exception {
+ var innerMessage: String? = null
abstract var errorId: Int
var parameters: (Array)? = null
+ var mThrowable: Throwable? = null
constructor() : super()
constructor(message: String) : super(message)
- constructor(message: String, throwable: Throwable) : super(message, throwable)
- constructor(throwable: Throwable) : super(throwable)
+ constructor(message: String, throwable: Throwable) {
+ mThrowable = throwable
+ innerMessage = StringBuilder().apply {
+ append(message)
+ if (throwable.localizedMessage != null) {
+ append(" ")
+ append(throwable.localizedMessage)
+ }
+ }.toString()
+ }
+ constructor(throwable: Throwable) {
+ mThrowable = throwable
+ innerMessage = throwable.localizedMessage
+ }
fun getLocalizedMessage(resources: Resources): String {
- parameters?.let {
- return resources.getString(errorId, *it)
- } ?: return resources.getString(errorId)
+ val throwable = mThrowable
+ if (throwable is DatabaseException)
+ errorId = throwable.errorId
+ val localMessage = parameters?.let {
+ resources.getString(errorId, *it)
+ } ?: resources.getString(errorId)
+ return StringBuilder().apply {
+ append(localMessage)
+ if (innerMessage != null) {
+ append(" ")
+ append(innerMessage)
+ }
+ }.toString()
+ }
+
+ override fun printStackTrace() {
+ mThrowable?.printStackTrace()
+ super.printStackTrace()
+ }
+
+ override fun printStackTrace(s: PrintStream) {
+ mThrowable?.printStackTrace(s)
+ super.printStackTrace(s)
+ }
+
+ override fun printStackTrace(s: PrintWriter) {
+ mThrowable?.printStackTrace(s)
+ super.printStackTrace(s)
}
}
-open class LoadDatabaseException : DatabaseException {
+class FileNotFoundDatabaseException : DatabaseInputException() {
@StringRes
- override var errorId: Int = R.string.error_load_database
- constructor() : super()
- constructor(string: String) : super(string)
- constructor(throwable: Throwable) : super(throwable)
+ override var errorId: Int = R.string.file_not_found_content
}
-class FileNotFoundDatabaseException : LoadDatabaseException {
+class CorruptedDatabaseException : DatabaseInputException() {
@StringRes
- override var errorId: Int = R.string.file_not_found_content
- constructor() : super()
- constructor(string: String) : super(string)
- constructor(exception: Throwable) : super(exception)
+ override var errorId: Int = R.string.corrupted_file
}
-class InvalidAlgorithmDatabaseException : LoadDatabaseException {
+class InvalidAlgorithmDatabaseException : DatabaseInputException {
@StringRes
override var errorId: Int = R.string.invalid_algorithm
constructor() : super()
constructor(exception: Throwable) : super(exception)
}
-class DuplicateUuidDatabaseException: LoadDatabaseException {
+class UnknownDatabaseLocationException : DatabaseException() {
@StringRes
- override var errorId: Int = R.string.invalid_db_same_uuid
- constructor(type: Type, uuid: NodeId<*>) : super() {
- parameters = arrayOf(type.name, uuid.toString())
- }
- constructor(exception: Throwable) : super(exception)
+ override var errorId: Int = R.string.error_location_unknown
}
-class IODatabaseException : LoadDatabaseException {
+class HardwareKeyDatabaseException : DatabaseException() {
@StringRes
- override var errorId: Int = R.string.error_load_database
- constructor() : super()
- constructor(string: String) : super(string)
- constructor(exception: Throwable) : super(exception)
+ override var errorId: Int = R.string.error_hardware_key_unsupported
}
-class KDFMemoryDatabaseException : LoadDatabaseException {
+class EmptyKeyDatabaseException : DatabaseException() {
@StringRes
- override var errorId: Int = R.string.error_load_database_KDF_memory
- constructor() : super()
- constructor(exception: Throwable) : super(exception)
+ override var errorId: Int = R.string.error_empty_key
}
-class SignatureDatabaseException : LoadDatabaseException {
+class SignatureDatabaseException : DatabaseInputException() {
@StringRes
override var errorId: Int = R.string.invalid_db_sig
- constructor() : super()
- constructor(exception: Throwable) : super(exception)
}
-class VersionDatabaseException : LoadDatabaseException {
+class VersionDatabaseException : DatabaseInputException() {
@StringRes
override var errorId: Int = R.string.unsupported_db_version
- constructor() : super()
- constructor(exception: Throwable) : super(exception)
}
-class InvalidCredentialsDatabaseException : LoadDatabaseException {
+class InvalidCredentialsDatabaseException : DatabaseInputException {
@StringRes
override var errorId: Int = R.string.invalid_credentials
constructor() : super()
- constructor(exception: Throwable) : super(exception)
+ constructor(string: String) : super(string)
}
-class NoMemoryDatabaseException: LoadDatabaseException {
+class KDFMemoryDatabaseException(exception: Throwable) : DatabaseInputException(exception) {
+ @StringRes
+ override var errorId: Int = R.string.error_load_database_KDF_memory
+}
+
+class NoMemoryDatabaseException(exception: Throwable) : DatabaseInputException(exception) {
@StringRes
override var errorId: Int = R.string.error_out_of_memory
+}
+
+class DuplicateUuidDatabaseException(type: Type, uuid: NodeId<*>) : DatabaseInputException() {
+ @StringRes
+ override var errorId: Int = R.string.invalid_db_same_uuid
+ init {
+ parameters = arrayOf(type.name, uuid.toString())
+ }
+}
+
+class XMLMalformedDatabaseException : DatabaseInputException {
+ @StringRes
+ override var errorId: Int = R.string.error_XML_malformed
constructor() : super()
- constructor(exception: Throwable) : super(exception)
+ constructor(string: String) : super(string)
}
-class MoveEntryDatabaseException: LoadDatabaseException {
+class MergeDatabaseKDBException : DatabaseInputException() {
+ @StringRes
+ override var errorId: Int = R.string.error_unable_merge_database_kdb
+}
+
+class MoveEntryDatabaseException : DatabaseException() {
@StringRes
override var errorId: Int = R.string.error_move_entry_here
- constructor() : super()
- constructor(exception: Throwable) : super(exception)
}
-class MoveGroupDatabaseException: LoadDatabaseException {
+class MoveGroupDatabaseException : DatabaseException() {
@StringRes
override var errorId: Int = R.string.error_move_group_here
- constructor() : super()
- constructor(exception: Throwable) : super(exception)
}
-class CopyEntryDatabaseException: LoadDatabaseException {
+class CopyEntryDatabaseException : DatabaseException() {
@StringRes
override var errorId: Int = R.string.error_copy_entry_here
- constructor() : super()
- constructor(exception: Throwable) : super(exception)
}
-class CopyGroupDatabaseException: LoadDatabaseException {
+class CopyGroupDatabaseException : DatabaseException() {
@StringRes
override var errorId: Int = R.string.error_copy_group_here
+}
+
+open class DatabaseInputException : DatabaseException {
+ @StringRes
+ override var errorId: Int = R.string.error_load_database
constructor() : super()
- constructor(exception: Throwable) : super(exception)
+ constructor(string: String) : super(string)
+ constructor(throwable: Throwable) : super(throwable)
}
-// TODO Output Exception
open class DatabaseOutputException : DatabaseException {
@StringRes
override var errorId: Int = R.string.error_save_database
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt
index 321876af4..f8d4f551d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInput.kt
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.file.input
import android.util.Log
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
-import com.kunzisoft.keepass.database.exception.LoadDatabaseException
+import com.kunzisoft.keepass.database.exception.DatabaseInputException
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import java.io.InputStream
@@ -33,15 +33,9 @@ abstract class DatabaseInput> (protected var m
/**
* Load a versioned database file, return contents in a new DatabaseVersioned.
- *
- * @param databaseInputStream Existing file to load.
- * @param password Pass phrase for infile.
- * @return new DatabaseVersioned container.
- *
- * @throws LoadDatabaseException on database error (contains IO exceptions)
*/
- @Throws(LoadDatabaseException::class)
+ @Throws(DatabaseInputException::class)
abstract fun openDatabase(databaseInputStream: InputStream,
progressTaskUpdater: ProgressTaskUpdater?,
assignMasterKey: (() -> Unit)): D
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt
index 8d9373ecd..c4ff17da9 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDB.kt
@@ -47,7 +47,7 @@ import javax.crypto.CipherInputStream
class DatabaseInputKDB(database: DatabaseKDB)
: DatabaseInput(database) {
- @Throws(LoadDatabaseException::class)
+ @Throws(DatabaseInputException::class)
override fun openDatabase(databaseInputStream: InputStream,
progressTaskUpdater: ProgressTaskUpdater?,
assignMasterKey: (() -> Unit)): DatabaseKDB {
@@ -76,6 +76,7 @@ class DatabaseInputKDB(database: DatabaseKDB)
throw VersionDatabaseException()
}
+ mDatabase.transformSeed = header.transformSeed
assignMasterKey.invoke()
// Select algorithm
@@ -310,18 +311,11 @@ class DatabaseInputKDB(database: DatabaseKDB)
stopContentTimer()
- } catch (e: LoadDatabaseException) {
+ } catch (e: Error) {
mDatabase.clearAll()
- throw e
- } catch (e: IOException) {
- mDatabase.clearAll()
- throw IODatabaseException(e)
- } catch (e: OutOfMemoryError) {
- mDatabase.clearAll()
- throw NoMemoryDatabaseException(e)
- } catch (e: Exception) {
- mDatabase.clearAll()
- throw LoadDatabaseException(e)
+ if (e is OutOfMemoryError)
+ throw NoMemoryDatabaseException(e)
+ throw DatabaseInputException(e)
}
return mDatabase
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt
index ed6e748c7..ec0ca0e50 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt
@@ -99,7 +99,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
this.isRAMSufficient = method
}
- @Throws(LoadDatabaseException::class)
+ @Throws(DatabaseInputException::class)
override fun openDatabase(databaseInputStream: InputStream,
progressTaskUpdater: ProgressTaskUpdater?,
assignMasterKey: (() -> Unit)): DatabaseKDBX {
@@ -114,6 +114,8 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
hashOfHeader = headerAndHash.hash
val pbHeader = headerAndHash.header
+ val transformSeed = header.transformSeed
+ mDatabase.transformSeed = transformSeed
assignMasterKey.invoke()
mDatabase.makeFinalKey(header.masterSeed)
@@ -155,7 +157,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
throw InvalidCredentialsDatabaseException()
}
- val hmacKey = mDatabase.hmacKey ?: throw LoadDatabaseException()
+ val hmacKey = mDatabase.hmacKey ?: throw DatabaseInputException()
val blockKey = HmacBlock.getHmacKey64(hmacKey, UnsignedLong.MAX_BYTES)
val hmac: Mac = HmacBlock.getHmacSha256(blockKey)
@@ -187,7 +189,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
try {
randomStream = CrsAlgorithm.getCipher(header.innerRandomStream, header.innerRandomStreamKey)
} catch (e: Exception) {
- throw LoadDatabaseException(e)
+ throw DatabaseInputException(e)
}
val xmlPullParserFactory = XmlPullParserFactory.newInstance().apply {
@@ -200,19 +202,12 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
stopContentTimer()
- } catch (e: LoadDatabaseException) {
- throw e
- } catch (e: XmlPullParserException) {
- throw IODatabaseException(e)
- } catch (e: IOException) {
+ } catch (e: Error) {
+ if (e is OutOfMemoryError)
+ throw NoMemoryDatabaseException(e)
if (e.message?.contains("Hash failed with code") == true)
throw KDFMemoryDatabaseException(e)
- else
- throw IODatabaseException(e)
- } catch (e: OutOfMemoryError) {
- throw NoMemoryDatabaseException(e)
- } catch (e: Exception) {
- throw LoadDatabaseException(e)
+ throw DatabaseInputException(e)
}
return mDatabase
@@ -227,7 +222,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
val fieldId = dataInputStream.read().toByte()
val size = dataInputStream.readBytes4ToUInt().toKotlinInt()
- if (size < 0) throw IOException("Corrupted file")
+ if (size < 0) throw CorruptedDatabaseException()
var data = ByteArray(0)
try {
@@ -238,7 +233,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
}
} catch (e: Exception) {
// OOM only if corrupted file
- throw IOException("Corrupted file")
+ throw CorruptedDatabaseException()
}
readStream = true
@@ -297,7 +292,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
Binaries
}
- @Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
+ @Throws(XmlPullParserException::class, IOException::class, DatabaseInputException::class)
private fun readDocumentStreamed(xpp: XmlPullParser) {
ctxGroups.clear()
@@ -324,11 +319,11 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
}
// Error checks
- if (ctx != KdbContext.Null) throw IOException("Malformed")
- if (ctxGroups.size != 0) throw IOException("Malformed")
+ if (ctx != KdbContext.Null) throw XMLMalformedDatabaseException()
+ if (ctxGroups.size != 0) throw XMLMalformedDatabaseException()
}
- @Throws(XmlPullParserException::class, IOException::class, LoadDatabaseException::class)
+ @Throws(XmlPullParserException::class, IOException::class, DatabaseInputException::class)
private fun readXmlElement(ctx: KdbContext, xpp: XmlPullParser): KdbContext {
val name = xpp.name
when (ctx) {
@@ -352,7 +347,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
if (encodedHash.isNotEmpty() && hashOfHeader != null) {
val hash = Base64.decode(encodedHash, BASE_64_FLAG)
if (!Arrays.equals(hash, hashOfHeader)) {
- throw LoadDatabaseException()
+ throw DatabaseInputException()
}
}
} else if (name.equals(DatabaseKDBXXML.ElemSettingsChanged, ignoreCase = true)) {
@@ -824,7 +819,7 @@ class DatabaseInputKDBX(database: DatabaseKDBX)
if (ctx != null) {
contextName = ctx.name
}
- throw RuntimeException("Invalid end element: Context " + contextName + "End element: " + name)
+ throw XMLMalformedDatabaseException("Invalid end element: Context " + contextName + "End element: " + name)
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt
index 4d456e538..e2d7a70e6 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutput.kt
@@ -26,7 +26,7 @@ import java.io.OutputStream
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
-abstract class DatabaseOutput protected constructor(protected var mOutputStream: OutputStream) {
+abstract class DatabaseOutput {
@Throws(DatabaseOutputException::class)
protected open fun setIVs(header: Header): SecureRandom {
@@ -44,9 +44,7 @@ abstract class DatabaseOutput protected constructor(pro
}
@Throws(DatabaseOutputException::class)
- abstract fun output()
-
- @Throws(DatabaseOutputException::class)
- abstract fun outputHeader(outputStream: OutputStream): Header
+ abstract fun writeDatabase(outputStream: OutputStream,
+ assignMasterKey: () -> Unit)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt
index 96cfe0c1c..83333ec2d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDB.kt
@@ -39,9 +39,8 @@ import java.security.*
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
-class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
- outputStream: OutputStream)
- : DatabaseOutput(outputStream) {
+class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB)
+ : DatabaseOutput() {
private var headerHashBlock: ByteArray? = null
@@ -60,15 +59,15 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
}
@Throws(DatabaseOutputException::class)
- override fun output() {
+ override fun writeDatabase(outputStream: OutputStream,
+ assignMasterKey: () -> Unit) {
// Before we output the header, we should sort our list of groups
// and remove any orphaned nodes that are no longer part of the tree hierarchy
// also remove the virtual root not present in kdb
val rootGroup = mDatabaseKDB.rootGroup
sortNodesForOutput()
- val header = outputHeader(mOutputStream)
-
+ val header = outputHeader(outputStream, assignMasterKey)
val finalKey = getFinalKey(header)
val cipher: Cipher = try {
@@ -81,7 +80,7 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
}
try {
- val cos = CipherOutputStream(mOutputStream, cipher)
+ val cos = CipherOutputStream(outputStream, cipher)
val bos = BufferedOutputStream(cos)
outputPlanGroupAndEntries(bos)
bos.flush()
@@ -107,7 +106,8 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
}
@Throws(DatabaseOutputException::class)
- override fun outputHeader(outputStream: OutputStream): DatabaseHeaderKDB {
+ private fun outputHeader(outputStream: OutputStream,
+ assignMasterKey: () -> Unit): DatabaseHeaderKDB {
// Build header
val header = DatabaseHeaderKDB()
header.signature1 = DatabaseHeaderKDB.DBSIG_1
@@ -132,6 +132,9 @@ class DatabaseOutputKDB(private val mDatabaseKDB: DatabaseKDB,
setIVs(header)
+ mDatabaseKDB.transformSeed = header.transformSeed
+ assignMasterKey()
+
// Header checksum
val headerDigest: MessageDigest = HashManager.getHash256()
diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt
index 241fc10df..66e6afa16 100644
--- a/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/database/file/output/DatabaseOutputKDBX.kt
@@ -56,9 +56,8 @@ import javax.crypto.CipherOutputStream
import kotlin.experimental.or
-class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
- outputStream: OutputStream)
- : DatabaseOutput(outputStream) {
+class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX)
+ : DatabaseOutput() {
private var randomStream: StreamCipher? = null
private lateinit var xml: XmlSerializer
@@ -67,43 +66,34 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
private var headerHmac: ByteArray? = null
@Throws(DatabaseOutputException::class)
- override fun output() {
+ override fun writeDatabase(outputStream: OutputStream,
+ assignMasterKey: () -> Unit) {
try {
- header = outputHeader(mOutputStream)
+ header = outputHeader(outputStream, assignMasterKey)
val osPlain: OutputStream = if (header!!.version.isBefore(FILE_VERSION_40)) {
- val cos = attachStreamEncryptor(header!!, mOutputStream)
+ val cos = attachStreamEncryptor(header!!, outputStream)
cos.write(header!!.streamStartBytes)
HashedBlockOutputStream(cos)
} else {
- mOutputStream.write(hashOfHeader!!)
- mOutputStream.write(headerHmac!!)
+ outputStream.write(hashOfHeader!!)
+ outputStream.write(headerHmac!!)
- attachStreamEncryptor(header!!, HmacBlockOutputStream(mOutputStream, mDatabaseKDBX.hmacKey!!))
+ attachStreamEncryptor(header!!, HmacBlockOutputStream(outputStream, mDatabaseKDBX.hmacKey!!))
}
- val xmlOutputStream: OutputStream
- try {
- xmlOutputStream = when(mDatabaseKDBX.compressionAlgorithm) {
- CompressionAlgorithm.GZip -> GZIPOutputStream(osPlain)
- else -> osPlain
- }
-
+ when(mDatabaseKDBX.compressionAlgorithm) {
+ CompressionAlgorithm.GZip -> GZIPOutputStream(osPlain)
+ else -> osPlain
+ }.use { xmlOutputStream ->
if (!header!!.version.isBefore(FILE_VERSION_40)) {
outputInnerHeader(mDatabaseKDBX, header!!, xmlOutputStream)
}
-
outputDatabase(xmlOutputStream)
- xmlOutputStream.close()
- } catch (e: IllegalArgumentException) {
- throw DatabaseOutputException(e)
- } catch (e: IllegalStateException) {
- throw DatabaseOutputException(e)
}
-
- } catch (e: IOException) {
+ } catch (e: Exception) {
throw DatabaseOutputException(e)
}
}
@@ -322,11 +312,15 @@ class DatabaseOutputKDBX(private val mDatabaseKDBX: DatabaseKDBX,
}
@Throws(DatabaseOutputException::class)
- override fun outputHeader(outputStream: OutputStream): DatabaseHeaderKDBX {
+ private fun outputHeader(outputStream: OutputStream,
+ assignMasterKey: () -> Unit): DatabaseHeaderKDBX {
try {
val header = DatabaseHeaderKDBX(mDatabaseKDBX)
setIVs(header)
+ mDatabaseKDBX.transformSeed = header.transformSeed
+ assignMasterKey.invoke()
+
val pho = DatabaseHeaderOutputKDBX(mDatabaseKDBX, header, outputStream)
pho.output()
diff --git a/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKey.kt b/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKey.kt
new file mode 100644
index 000000000..e4249a7e7
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKey.kt
@@ -0,0 +1,35 @@
+package com.kunzisoft.keepass.hardware
+
+enum class HardwareKey(val value: String) {
+ // FIDO2_SECRET("FIDO2 secret"),
+ CHALLENGE_RESPONSE_YUBIKEY("Yubikey challenge-response");
+
+ override fun toString(): String {
+ return value
+ }
+
+ companion object {
+ val DEFAULT = CHALLENGE_RESPONSE_YUBIKEY
+
+ fun getStringValues(): List {
+ return values().map { it.value }
+ }
+
+ fun fromPosition(position: Int): HardwareKey {
+ return when (position) {
+ // 0 -> FIDO2_SECRET
+ 0 -> CHALLENGE_RESPONSE_YUBIKEY
+ else -> DEFAULT
+ }
+ }
+
+ fun getHardwareKeyFromString(text: String?): HardwareKey? {
+ if (text == null)
+ return null
+ values().find { it.value == text }?.let {
+ return it
+ }
+ return DEFAULT
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKeyActivity.kt b/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKeyActivity.kt
new file mode 100644
index 000000000..e337739ae
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/hardware/HardwareKeyActivity.kt
@@ -0,0 +1,172 @@
+package com.kunzisoft.keepass.hardware
+
+import android.app.Activity
+import android.content.Context
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.util.Log
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.ActivityResultCallback
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AlertDialog
+import com.kunzisoft.keepass.BuildConfig
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
+import com.kunzisoft.keepass.database.element.Database
+import com.kunzisoft.keepass.utils.UriUtil
+
+/**
+ * Special activity to deal with hardware key drivers,
+ * return the response to the database service once finished
+ */
+class HardwareKeyActivity: DatabaseModeActivity(){
+
+ // To manage hardware key challenge response
+ private val resultCallback = ActivityResultCallback { result ->
+ if (result.resultCode == Activity.RESULT_OK) {
+ val challengeResponse: ByteArray? = result.data?.getByteArrayExtra(HARDWARE_KEY_RESPONSE_KEY)
+ Log.d(TAG, "Response form challenge")
+ mDatabaseTaskProvider?.startChallengeResponded(challengeResponse ?: ByteArray(0))
+ } else {
+ Log.e(TAG, "Response from challenge error")
+ mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0))
+ }
+ finish()
+ }
+
+ private var activityResultLauncher: ActivityResultLauncher = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult(),
+ resultCallback
+ )
+
+ override fun applyCustomStyle(): Boolean {
+ return false
+ }
+
+ override fun showDatabaseDialog(): Boolean {
+ return false
+ }
+
+ override fun onDatabaseRetrieved(database: Database?) {
+ super.onDatabaseRetrieved(database)
+
+ val hardwareKey = HardwareKey.getHardwareKeyFromString(
+ intent.getStringExtra(DATA_HARDWARE_KEY)
+ )
+ if (isHardwareKeyAvailable(this, hardwareKey, true) {
+ mDatabaseTaskProvider?.startChallengeResponded(ByteArray(0))
+ }) {
+ when (hardwareKey) {
+ /*
+ HardwareKey.FIDO2_SECRET -> {
+ // TODO FIDO2 under development
+ throw Exception("FIDO2 not implemented")
+ }
+ */
+ HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
+ launchYubikeyChallengeForResponse(intent.getByteArrayExtra(DATA_SEED))
+ }
+ else -> {
+ finish()
+ }
+ }
+ }
+ }
+
+ private fun launchYubikeyChallengeForResponse(seed: ByteArray?) {
+ // Transform the seed before sending
+ var challenge: ByteArray? = null
+ if (seed != null) {
+ challenge = ByteArray(64)
+ seed.copyInto(challenge, 0, 0, 32)
+ challenge.fill(32, 32, 64)
+ }
+ // Send to the driver
+ activityResultLauncher.launch(
+ Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT).apply {
+ putExtra(HARDWARE_KEY_CHALLENGE_KEY, challenge)
+ }
+ )
+ Log.d(TAG, "Challenge sent")
+ }
+
+ companion object {
+ private val TAG = HardwareKeyActivity::class.java.simpleName
+
+ private const val DATA_HARDWARE_KEY = "DATA_HARDWARE_KEY"
+ private const val DATA_SEED = "DATA_SEED"
+ private const val YUBIKEY_CHALLENGE_RESPONSE_INTENT = "android.yubikey.intent.action.CHALLENGE_RESPONSE"
+ private const val HARDWARE_KEY_CHALLENGE_KEY = "challenge"
+ private const val HARDWARE_KEY_RESPONSE_KEY = "response"
+
+ fun launchHardwareKeyActivity(
+ context: Context,
+ hardwareKey: HardwareKey,
+ seed: ByteArray?
+ ) {
+ context.startActivity(Intent(context, HardwareKeyActivity::class.java).apply {
+ flags = FLAG_ACTIVITY_NEW_TASK
+ putExtra(DATA_HARDWARE_KEY, hardwareKey.value)
+ putExtra(DATA_SEED, seed)
+ })
+ }
+
+ fun isHardwareKeyAvailable(
+ context: Context,
+ hardwareKey: HardwareKey?,
+ showDialog: Boolean = true,
+ onDialogDismissed: DialogInterface.OnDismissListener? = null
+ ): Boolean {
+ if (hardwareKey == null)
+ return false
+ return when (hardwareKey) {
+ /*
+ HardwareKey.FIDO2_SECRET -> {
+ // TODO FIDO2 under development
+ if (showDialog)
+ UnderDevelopmentFeatureDialogFragment()
+ .show(activity.supportFragmentManager, "underDevFeatureDialog")
+ false
+ }
+ */
+ HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
+ // Check available intent
+ val yubikeyDriverAvailable =
+ Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT)
+ .resolveActivity(context.packageManager) != null
+ if (showDialog && !yubikeyDriverAvailable
+ && context is Activity)
+ showHardwareKeyDriverNeeded(context, hardwareKey) {
+ onDialogDismissed?.onDismiss(it)
+ context.finish()
+ }
+ yubikeyDriverAvailable
+ }
+ }
+ }
+
+ private fun showHardwareKeyDriverNeeded(
+ context: Context,
+ hardwareKey: HardwareKey,
+ onDialogDismissed: DialogInterface.OnDismissListener
+ ) {
+ val builder = AlertDialog.Builder(context)
+ builder
+ .setMessage(
+ context.getString(R.string.error_driver_required, hardwareKey.toString())
+ )
+ .setPositiveButton(R.string.download) { _, _ ->
+ UriUtil.openExternalApp(
+ context,
+ context.getString(R.string.key_driver_app_id),
+ context.getString(R.string.key_driver_url)
+ )
+ }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
+ .setOnDismissListener(onDialogDismissed)
+ builder.create().show()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/CipherEncryptDatabase.kt b/app/src/main/java/com/kunzisoft/keepass/model/CipherEncryptDatabase.kt
index f3ed3dade..321facd97 100644
--- a/app/src/main/java/com/kunzisoft/keepass/model/CipherEncryptDatabase.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/model/CipherEncryptDatabase.kt
@@ -41,11 +41,6 @@ class CipherEncryptDatabase(): Parcelable {
parcel.readByteArray(specParameters)
}
- fun replaceContent(copy: CipherEncryptDatabase) {
- this.encryptedValue = copy.encryptedValue
- this.specParameters = copy.specParameters
- }
-
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(databaseUri, flags)
parcel.writeEnum(credentialStorage)
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt b/app/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt
index 578fc4075..92470c2fe 100644
--- a/app/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt
@@ -1,9 +1,11 @@
package com.kunzisoft.keepass.model
import android.net.Uri
+import com.kunzisoft.keepass.hardware.HardwareKey
data class DatabaseFile(var databaseUri: Uri? = null,
var keyFileUri: Uri? = null,
+ var hardwareKey: HardwareKey? = null,
var databaseDecodedPath: String? = null,
var databaseAlias: String? = null,
var databaseFileExists: Boolean = false,
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/MainCredential.kt b/app/src/main/java/com/kunzisoft/keepass/model/MainCredential.kt
deleted file mode 100644
index e5ee12c72..000000000
--- a/app/src/main/java/com/kunzisoft/keepass/model/MainCredential.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.kunzisoft.keepass.model
-
-import android.net.Uri
-import android.os.Parcel
-import android.os.Parcelable
-
-data class MainCredential(var masterPassword: String? = null, var keyFileUri: Uri? = null): Parcelable {
-
- constructor(parcel: Parcel) : this(
- parcel.readString(),
- parcel.readParcelable(Uri::class.java.classLoader)) {
- }
-
- override fun writeToParcel(parcel: Parcel, flags: Int) {
- parcel.writeString(masterPassword)
- parcel.writeParcelable(keyFileUri, flags)
- }
-
- override fun describeContents(): Int {
- return 0
- }
-
- companion object CREATOR : Parcelable.Creator {
- override fun createFromParcel(parcel: Parcel): MainCredential {
- return MainCredential(parcel)
- }
-
- override fun newArray(size: Int): Array {
- return arrayOfNulls(size)
- }
- }
-}
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/ProgressMessage.kt b/app/src/main/java/com/kunzisoft/keepass/model/ProgressMessage.kt
new file mode 100644
index 000000000..4b24b0d10
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/model/ProgressMessage.kt
@@ -0,0 +1,13 @@
+package com.kunzisoft.keepass.model
+
+import androidx.annotation.StringRes
+
+data class ProgressMessage(
+ @StringRes
+ var titleId: Int,
+ @StringRes
+ var messageId: Int? = null,
+ @StringRes
+ var warningId: Int? = null,
+ var cancelable: (() -> Unit)? = null
+)
diff --git a/app/src/main/java/com/kunzisoft/keepass/model/SnapFileDatabaseInfo.kt b/app/src/main/java/com/kunzisoft/keepass/model/SnapFileDatabaseInfo.kt
index 6d6bbbbd9..fa6650ff8 100644
--- a/app/src/main/java/com/kunzisoft/keepass/model/SnapFileDatabaseInfo.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/model/SnapFileDatabaseInfo.kt
@@ -1,3 +1,22 @@
+/*
+ * Copyright 2021 Jeremy Jamet / Kunzisoft.
+ *
+ * This file is part of KeePassDX.
+ *
+ * KeePassDX 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.
+ *
+ * KeePassDX 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 KeePassDX. If not, see .
+ *
+ */
package com.kunzisoft.keepass.model
import android.content.Context
diff --git a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt
index fb04544a7..d8a70e9e9 100644
--- a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt
@@ -27,6 +27,7 @@ import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.util.Log
+import androidx.annotation.StringRes
import androidx.media.app.NotificationCompat
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.GroupActivity
@@ -37,12 +38,15 @@ import com.kunzisoft.keepass.database.action.node.*
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.Group
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
+import com.kunzisoft.keepass.hardware.HardwareKey
+import com.kunzisoft.keepass.hardware.HardwareKeyActivity
import com.kunzisoft.keepass.model.CipherEncryptDatabase
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.model.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
@@ -53,6 +57,7 @@ import com.kunzisoft.keepass.utils.LOCK_ACTION
import com.kunzisoft.keepass.utils.closeDatabase
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.Channel
import java.util.*
open class DatabaseTaskNotificationService : LockNotificationService(), ProgressTaskUpdater {
@@ -61,20 +66,24 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private var mDatabase: Database? = null
+ // File description
+ private var mSnapFileDatabaseInfo: SnapFileDatabaseInfo? = null
+ private var mLastLocalSaveTime: Long = 0
+
private val mainScope = CoroutineScope(Dispatchers.Main)
- private var mDatabaseListeners = LinkedList()
- private var mDatabaseInfoListeners = LinkedList()
+ private var mDatabaseListeners = mutableListOf()
+ private var mDatabaseInfoListeners = mutableListOf()
private var mActionTaskBinder = ActionTaskBinder()
- private var mActionTaskListeners = LinkedList()
+ private var mActionTaskListeners = mutableListOf()
+ // Channel to connect asynchronously a response
+ private var mResponseChallengeChannel: Channel? = null
+
private var mActionRunning = false
private var mTaskRemovedRequested = false
- private var mCreationState = false
+ private var mSaveState = false
- private var mIconId: Int = R.drawable.notification_ic_database_load
- private var mTitleId: Int = R.string.database_opened
- private var mMessageId: Int? = null
- private var mWarningId: Int? = null
+ private var mProgressMessage: ProgressMessage = ProgressMessage(R.string.database_opened)
override fun retrieveChannelId(): String {
return CHANNEL_DATABASE_ID
@@ -126,9 +135,20 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
interface ActionTaskListener {
- fun onStartAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?)
- fun onUpdateAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?)
- fun onStopAction(database: Database, actionTask: String, result: ActionRunnable.Result)
+ fun onStartAction(database: Database,
+ progressMessage: ProgressMessage)
+ fun onUpdateAction(database: Database,
+ progressMessage: ProgressMessage)
+ fun onStopAction(database: Database,
+ actionTask: String,
+ result: ActionRunnable.Result)
+ }
+
+ interface RequestChallengeListener {
+ fun onChallengeResponseRequested(
+ hardwareKey: HardwareKey,
+ seed: ByteArray?
+ )
}
fun checkDatabase() {
@@ -165,7 +185,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
Log.i(TAG, "Database file modified " +
"$previousDatabaseInfo != $lastFileDatabaseInfo ")
// Call listener to indicate a change in database info
- if (!mCreationState && previousDatabaseInfo != null) {
+ if (!mSaveState && previousDatabaseInfo != null) {
mDatabaseInfoListeners.forEach { listener ->
listener.onDatabaseInfoChanged(previousDatabaseInfo, lastFileDatabaseInfo)
}
@@ -197,12 +217,47 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
mDatabase?.let { database ->
if (mActionRunning) {
mActionTaskListeners.forEach { actionTaskListener ->
- actionTaskListener.onStartAction(database, mTitleId, mMessageId, mWarningId)
+ actionTaskListener.onStartAction(
+ database, mProgressMessage
+ )
}
}
}
}
+ @OptIn(ExperimentalCoroutinesApi::class)
+ private fun sendResponseToChallenge(response: ByteArray) {
+ mainScope.launch {
+ val responseChannel = mResponseChallengeChannel
+ if (responseChannel == null || responseChannel.isEmpty) {
+ if (response.isEmpty()) {
+ cancelChallengeResponse(R.string.error_no_response_from_challenge)
+ } else {
+ mResponseChallengeChannel?.send(response)
+ }
+ } else {
+ cancelChallengeResponse(R.string.error_response_already_provided)
+ }
+ }
+ }
+
+ private fun initializeChallengeResponse() {
+ // Init the channels
+ if (mResponseChallengeChannel == null) {
+ mResponseChallengeChannel = Channel(0)
+ }
+ }
+
+ private fun closeChallengeResponse() {
+ mResponseChallengeChannel?.close()
+ mResponseChallengeChannel = null
+ }
+
+ private fun cancelChallengeResponse(@StringRes error: Int) {
+ mResponseChallengeChannel?.cancel(CancellationException(getString(error)))
+ mResponseChallengeChannel = null
+ }
+
override fun onBind(intent: Intent): IBinder? {
super.onBind(intent)
return mActionTaskBinder
@@ -219,8 +274,20 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
+ // Get save state
+ mSaveState = if (intent != null) {
+ if (intent.hasExtra(SAVE_DATABASE_KEY)) {
+ !database.isReadOnly && intent.getBooleanExtra(
+ SAVE_DATABASE_KEY,
+ mSaveState
+ )
+ } else (intent.action == ACTION_DATABASE_CREATE_TASK
+ || intent.action == ACTION_DATABASE_ASSIGN_PASSWORD_TASK
+ || intent.action == ACTION_DATABASE_SAVE)
+ } else false
+
// Create the notification
- buildMessage(intent, database.isReadOnly)
+ buildNotification(intent)
val intentAction = intent?.action
@@ -258,7 +325,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK,
ACTION_DATABASE_UPDATE_PARALLELISM_TASK,
ACTION_DATABASE_UPDATE_ITERATIONS_TASK -> buildDatabaseUpdateElementActionTask(intent, database)
- ACTION_DATABASE_SAVE -> buildDatabaseSave(intent, database)
+ ACTION_DATABASE_SAVE -> buildDatabaseSaveActionTask(intent, database)
+ ACTION_CHALLENGE_RESPONDED -> buildChallengeRespondedActionTask(intent)
else -> null
}
@@ -272,15 +340,16 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
mActionRunning = true
sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply {
- putExtra(DATABASE_TASK_TITLE_KEY, mTitleId)
- putExtra(DATABASE_TASK_MESSAGE_KEY, mMessageId)
- putExtra(DATABASE_TASK_WARNING_KEY, mWarningId)
+ putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId)
+ putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.messageId)
+ putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warningId)
})
mActionTaskListeners.forEach { actionTaskListener ->
- actionTaskListener.onStartAction(database, mTitleId, mMessageId, mWarningId)
+ actionTaskListener.onStartAction(
+ database, mProgressMessage
+ )
}
-
},
{
actionRunnable
@@ -325,7 +394,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
try {
startService(Intent(applicationContext,
DatabaseTaskNotificationService::class.java))
- } catch (e: IllegalStateException) {}
+ } catch (e: IllegalStateException) {
+ Log.w(TAG, "Cannot restart the database task service", e)
+ }
}
}
mTaskRemovedRequested = false
@@ -353,61 +424,51 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
- private fun buildMessage(intent: Intent?, readOnly: Boolean) {
+ private fun buildNotification(intent: Intent?) {
// Assign elements for updates
val intentAction = intent?.action
- var saveAction = false
- if (intent != null && intent.hasExtra(SAVE_DATABASE_KEY)) {
- saveAction = !readOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, saveAction)
- }
-
- mIconId = if (intentAction == null)
+ // Get icon depending action state
+ val iconId = if (intentAction == null)
R.drawable.notification_ic_database_open
else
- R.drawable.notification_ic_database_load
+ R.drawable.notification_ic_database_action
- mTitleId = when {
- saveAction -> {
- R.string.saving_database
- }
- intentAction == null -> {
+ // Title depending on action
+ mProgressMessage.titleId =
+ if (intentAction == null) {
R.string.database_opened
- }
- else -> {
- when (intentAction) {
- ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
- ACTION_DATABASE_LOAD_TASK,
- ACTION_DATABASE_MERGE_TASK,
- ACTION_DATABASE_RELOAD_TASK -> R.string.loading_database
- ACTION_DATABASE_SAVE -> R.string.saving_database
- else -> {
+ } else when (intentAction) {
+ ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
+ ACTION_DATABASE_LOAD_TASK,
+ ACTION_DATABASE_MERGE_TASK,
+ ACTION_DATABASE_RELOAD_TASK -> R.string.loading_database
+ ACTION_DATABASE_ASSIGN_PASSWORD_TASK,
+ ACTION_DATABASE_SAVE -> R.string.saving_database
+ else -> {
+ if (mSaveState)
+ R.string.saving_database
+ else
R.string.command_execution
- }
}
}
- }
- mMessageId = when (intentAction) {
- ACTION_DATABASE_LOAD_TASK,
- ACTION_DATABASE_MERGE_TASK,
- ACTION_DATABASE_RELOAD_TASK -> null
- else -> null
- }
+ // Updated later
+ mProgressMessage.messageId = null
- mWarningId =
- if (!saveAction
- || intentAction == ACTION_DATABASE_LOAD_TASK
- || intentAction == ACTION_DATABASE_MERGE_TASK
- || intentAction == ACTION_DATABASE_RELOAD_TASK)
- null
- else
+ // Warning if data is saved
+ mProgressMessage.warningId =
+ if (mSaveState)
R.string.do_not_kill_app
+ else
+ null
val notificationBuilder = buildNewNotification().apply {
- setSmallIcon(mIconId)
+ setSmallIcon(iconId)
intent?.let {
- setContentTitle(getString(intent.getIntExtra(DATABASE_TASK_TITLE_KEY, mTitleId)))
+ setContentTitle(getString(
+ intent.getIntExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId))
+ )
}
setAutoCancel(false)
setContentIntent(null)
@@ -513,15 +574,21 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
- override fun updateMessage(resId: Int) {
- mMessageId = resId
+ private fun notifyProgressMessage() {
mDatabase?.let { database ->
mActionTaskListeners.forEach { actionTaskListener ->
- actionTaskListener.onUpdateAction(database, mTitleId, mMessageId, mWarningId)
+ actionTaskListener.onUpdateAction(
+ database, mProgressMessage
+ )
}
}
}
+ override fun updateMessage(resId: Int) {
+ mProgressMessage.messageId = resId
+ notifyProgressMessage()
+ }
+
override fun actionOnLock() {
if (!TimeoutHelper.temporarilyDisableLock) {
closeDatabase(mDatabase)
@@ -539,6 +606,43 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
super.onTaskRemoved(rootIntent)
}
+ private fun retrieveResponseFromChallenge(hardwareKey: HardwareKey,
+ seed: ByteArray?): ByteArray {
+ // Request a challenge - response
+ var response: ByteArray
+ runBlocking {
+ // Initialize the channels
+ initializeChallengeResponse()
+ val previousMessage = mProgressMessage.copy()
+ mProgressMessage.apply {
+ messageId = R.string.waiting_challenge_request
+ cancelable = {
+ cancelChallengeResponse(R.string.error_cancel_by_user)
+ }
+ }
+ // Send the request
+ notifyProgressMessage()
+ HardwareKeyActivity
+ .launchHardwareKeyActivity(
+ this@DatabaseTaskNotificationService,
+ hardwareKey,
+ seed
+ )
+ // Wait the response
+ mProgressMessage.apply {
+ messageId = R.string.waiting_challenge_response
+ }
+ notifyProgressMessage()
+ response = mResponseChallengeChannel?.receive() ?: byteArrayOf()
+ // Close channels
+ closeChallengeResponse()
+ // Restore previous message
+ mProgressMessage = previousMessage
+ notifyProgressMessage()
+ }
+ return response
+ }
+
private fun buildDatabaseCreateActionTask(intent: Intent, database: Database): ActionRunnable? {
if (intent.hasExtra(DATABASE_URI_KEY)
@@ -550,15 +654,16 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
if (databaseUri == null)
return null
- mCreationState = true
-
return CreateDatabaseRunnable(this,
database,
databaseUri,
getString(R.string.database_default_name),
getString(R.string.database),
getString(R.string.template_group_name),
- mainCredential
+ mainCredential,
+ { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
) { result ->
result.data = Bundle().apply {
putParcelable(DATABASE_URI_KEY, databaseUri)
@@ -586,17 +691,18 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
if (databaseUri == null)
return null
- mCreationState = false
-
return LoadDatabaseRunnable(
- this,
- database,
- databaseUri,
- mainCredential,
- readOnly,
- cipherEncryptDatabase,
- intent.getBooleanExtra(FIX_DUPLICATE_UUID_KEY, false),
- this
+ this,
+ database,
+ databaseUri,
+ mainCredential,
+ { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ },
+ readOnly,
+ cipherEncryptDatabase,
+ intent.getBooleanExtra(FIX_DUPLICATE_UUID_KEY, false),
+ this
) { result ->
// Add each info to reload database after thrown duplicate UUID exception
result.data = Bundle().apply {
@@ -623,9 +729,16 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
return MergeDatabaseRunnable(
this,
- database,
databaseToMergeUri,
databaseToMergeMainCredential,
+ { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ },
+ database,
+ !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
+ { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ },
this
) { result ->
// No need to add each info to reload database
@@ -653,7 +766,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
database,
databaseUri,
intent.getParcelableExtra(MAIN_CREDENTIAL_KEY) ?: MainCredential()
- )
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
} else {
null
}
@@ -687,7 +802,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
newGroup,
parent,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
- AfterActionNodesRunnable())
+ AfterActionNodesRunnable()
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -712,7 +830,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
oldGroup,
newGroup,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
- AfterActionNodesRunnable())
+ AfterActionNodesRunnable()
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -737,7 +858,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
newEntry,
parent,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
- AfterActionNodesRunnable())
+ AfterActionNodesRunnable()
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -762,7 +886,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
oldEntry,
newEntry,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
- AfterActionNodesRunnable())
+ AfterActionNodesRunnable()
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -783,7 +910,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
getListNodesFromBundle(database, intent.extras!!),
newParent,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
- AfterActionNodesRunnable())
+ AfterActionNodesRunnable()
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -804,7 +934,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
getListNodesFromBundle(database, intent.extras!!),
newParent,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
- AfterActionNodesRunnable())
+ AfterActionNodesRunnable()
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -820,7 +953,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
database,
getListNodesFromBundle(database, intent.extras!!),
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
- AfterActionNodesRunnable())
+ AfterActionNodesRunnable()
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
} else {
null
}
@@ -838,7 +974,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
database,
mainEntry,
intent.getIntExtra(ENTRY_HISTORY_POSITION_KEY, -1),
- !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false))
+ !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false)
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -857,7 +996,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
database,
mainEntry,
intent.getIntExtra(ENTRY_HISTORY_POSITION_KEY, -1),
- !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false))
+ !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false)
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
}
} else {
null
@@ -881,7 +1023,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
oldElement,
newElement,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false)
- ).apply {
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }.apply {
mAfterSaveDatabase = { result ->
result.data = intent.extras
}
@@ -897,7 +1041,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
return RemoveUnlinkedDataDatabaseRunnable(this,
database,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false)
- ).apply {
+ ) { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }.apply {
mAfterSaveDatabase = { result ->
result.data = intent.extras
}
@@ -911,7 +1057,11 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
return if (intent.hasExtra(SAVE_DATABASE_KEY)) {
return SaveDatabaseRunnable(this,
database,
- !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false)
+ !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
+ null,
+ { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ }
).apply {
mAfterSaveDatabase = { result ->
result.data = intent.extras
@@ -925,7 +1075,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
/**
* Save database without parameter
*/
- private fun buildDatabaseSave(intent: Intent, database: Database): ActionRunnable? {
+ private fun buildDatabaseSaveActionTask(intent: Intent, database: Database): ActionRunnable? {
return if (intent.hasExtra(SAVE_DATABASE_KEY)) {
var databaseCopyUri: Uri? = null
@@ -936,12 +1086,34 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
SaveDatabaseRunnable(this,
database,
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
+ null,
+ { hardwareKey, seed ->
+ retrieveResponseFromChallenge(hardwareKey, seed)
+ },
databaseCopyUri)
} else {
null
}
}
+ private fun buildChallengeRespondedActionTask(intent: Intent): ActionRunnable? {
+ return if (intent.hasExtra(DATA_BYTES)) {
+ object : ActionRunnable() {
+ override fun onStartRun() {}
+ override fun onActionRun() {
+ mainScope.launch {
+ intent.getByteArrayExtra(DATA_BYTES)?.let { response ->
+ sendResponseToChallenge(response)
+ }
+ }
+ }
+ override fun onFinishRun() {}
+ }
+ } else {
+ null
+ }
+ }
+
companion object {
private val TAG = DatabaseTaskNotificationService::class.java.name
@@ -978,6 +1150,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
const val ACTION_DATABASE_UPDATE_PARALLELISM_TASK = "ACTION_DATABASE_UPDATE_PARALLELISM_TASK"
const val ACTION_DATABASE_UPDATE_ITERATIONS_TASK = "ACTION_DATABASE_UPDATE_ITERATIONS_TASK"
const val ACTION_DATABASE_SAVE = "ACTION_DATABASE_SAVE"
+ const val ACTION_CHALLENGE_RESPONDED = "ACTION_CHALLENGE_RESPONDED"
const val DATABASE_TASK_TITLE_KEY = "DATABASE_TASK_TITLE_KEY"
const val DATABASE_TASK_MESSAGE_KEY = "DATABASE_TASK_MESSAGE_KEY"
@@ -1001,9 +1174,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
const val NEW_NODES_KEY = "NEW_NODES_KEY"
const val OLD_ELEMENT_KEY = "OLD_ELEMENT_KEY" // Warning type of this thing change every time
const val NEW_ELEMENT_KEY = "NEW_ELEMENT_KEY" // Warning type of this thing change every time
-
- private var mSnapFileDatabaseInfo: SnapFileDatabaseInfo? = null
- private var mLastLocalSaveTime: Long = 0
+ const val DATA_BYTES = "DATA_BYTES"
fun getListNodesFromBundle(database: Database, bundle: Bundle): List {
val nodesAction = ArrayList()
diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt
index 3c2c7c0d8..1d0390ad7 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt
@@ -115,8 +115,8 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
mDatabaseViewModel.saveDatabase(save)
}
- private fun mergeDatabase() {
- mDatabaseViewModel.mergeDatabase(false)
+ private fun mergeDatabase(save: Boolean) {
+ mDatabaseViewModel.mergeDatabase(save)
}
private fun reloadDatabase() {
@@ -671,7 +671,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
true
}
R.id.menu_merge_database -> {
- mergeDatabase()
+ mergeDatabase(!mDatabaseReadOnly)
true
}
R.id.menu_reload_database -> {
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 77fcdc552..a0557c89e 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt
@@ -96,6 +96,12 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.remember_keyfile_locations_default))
}
+ fun rememberHardwareKey(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.remember_hardware_key_key),
+ context.resources.getBoolean(R.bool.remember_hardware_key_default))
+ }
+
fun automaticallyFocusSearch(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.auto_focus_search_key),
@@ -479,29 +485,33 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.enable_keep_screen_on_default))
}
+ fun isScreenshotModeEnabled(context: Context): Boolean {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ return prefs.getBoolean(context.getString(R.string.enable_screenshot_mode_key),
+ context.resources.getBoolean(R.bool.enable_screenshot_mode_key_default))
+ }
+
fun isAdvancedUnlockEnable(context: Context): Boolean {
return isBiometricUnlockEnable(context) || isDeviceCredentialUnlockEnable(context)
}
fun isBiometricUnlockEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
- val biometricSupported = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
- AdvancedUnlockManager.biometricUnlockSupported(context)
- } else {
- false
- }
return prefs.getBoolean(context.getString(R.string.biometric_unlock_enable_key),
context.resources.getBoolean(R.bool.biometric_unlock_enable_default))
- && biometricSupported
+ && (if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+ AdvancedUnlockManager.biometricUnlockSupported(context)
+ } else {
+ false
+ })
}
fun isDeviceCredentialUnlockEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
// Priority to biometric unlock
- val biometricAlreadySupported = isBiometricUnlockEnable(context)
return prefs.getBoolean(context.getString(R.string.device_credential_unlock_enable_key),
context.resources.getBoolean(R.bool.device_credential_unlock_enable_default))
- && !biometricAlreadySupported
+ && !isBiometricUnlockEnable(context)
}
fun isTempAdvancedUnlockEnable(context: Context): Boolean {
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 e748e92e8..3ddb6d60c 100644
--- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt
@@ -36,7 +36,7 @@ import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
import com.kunzisoft.keepass.database.element.Database
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
diff --git a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt
index 26bb26565..e9240b73e 100644
--- a/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/tasks/ProgressTaskDialogFragment.kt
@@ -24,15 +24,18 @@ import android.app.Dialog
import android.os.Bundle
import android.util.Log
import android.view.View
+import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R
-import java.lang.Exception
+import kotlinx.coroutines.launch
-open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
+open class ProgressTaskDialogFragment : DialogFragment() {
@StringRes
private var title = UNDEFINED
@@ -40,10 +43,12 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
private var message = UNDEFINED
@StringRes
private var warning = UNDEFINED
+ private var cancellable: (() -> Unit)? = null
private var titleView: TextView? = null
private var messageView: TextView? = null
private var warningView: TextView? = null
+ private var cancelButton: Button? = null
private var progressView: ProgressBar? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@@ -63,11 +68,13 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
titleView = root.findViewById(R.id.progress_dialog_title)
messageView = root.findViewById(R.id.progress_dialog_message)
warningView = root.findViewById(R.id.progress_dialog_warning)
+ cancelButton = root.findViewById(R.id.progress_dialog_cancel)
progressView = root.findViewById(R.id.progress_dialog_bar)
updateTitle(title)
updateMessage(message)
updateWarning(warning)
+ setCancellable(cancellable)
isCancelable = false
@@ -84,7 +91,7 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
}
private fun updateView(textView: TextView?, @StringRes resId: Int) {
- activity?.runOnUiThread {
+ activity?.lifecycleScope?.launch {
if (resId == UNDEFINED) {
textView?.visibility = View.GONE
} else {
@@ -94,21 +101,35 @@ open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
}
}
- fun updateTitle(@StringRes resId: Int) {
- this.title = resId
+ private fun updateCancelable() {
+ activity?.lifecycleScope?.launch {
+ cancelButton?.isVisible = cancellable != null
+ cancelButton?.setOnClickListener {
+ cancellable?.invoke()
+ }
+ }
+ }
+
+ fun updateTitle(@StringRes resId: Int?) {
+ this.title = resId ?: UNDEFINED
updateView(titleView, title)
}
- override fun updateMessage(@StringRes resId: Int) {
- this.message = resId
+ fun updateMessage(@StringRes resId: Int?) {
+ this.message = resId ?: UNDEFINED
updateView(messageView, message)
}
- fun updateWarning(@StringRes resId: Int) {
- this.warning = resId
+ fun updateWarning(@StringRes resId: Int?) {
+ this.warning = resId ?: UNDEFINED
updateView(warningView, warning)
}
+ fun setCancellable(cancellable: (() -> Unit)?) {
+ this.cancellable = cancellable
+ updateCancelable()
+ }
+
companion object {
private val TAG = ProgressTaskDialogFragment::class.java.simpleName
const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment"
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt
index d578df6ca..a794fa0ca 100644
--- a/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt
@@ -126,6 +126,26 @@ object ParcelableUtil {
}
}
+fun Parcel.readByteArrayCompat(): ByteArray? {
+ val dataLength = readInt()
+ return if (dataLength >= 0) {
+ val data = ByteArray(dataLength)
+ readByteArray(data)
+ data
+ } else {
+ null
+ }
+}
+
+fun Parcel.writeByteArrayCompat(data: ByteArray?) {
+ if (data != null) {
+ writeInt(data.size)
+ writeByteArray(data)
+ } else {
+ writeInt(-1)
+ }
+}
+
inline fun > Parcel.readEnum() =
readString()?.let { enumValueOf(it) }
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt
index 1ca58508e..c6c8428b3 100644
--- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt
@@ -28,6 +28,7 @@ import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.documentfile.provider.DocumentFile
+import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.education.Education
@@ -226,10 +227,10 @@ object UriUtil {
}
}
- fun getUriFromIntent(intent: Intent, key: String): Uri? {
+ fun getUriFromIntent(intent: Intent?, key: String): Uri? {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- val clipData = intent.clipData
+ val clipData = intent?.clipData
if (clipData != null) {
if (clipData.description.label == key) {
if (clipData.itemCount == 1) {
@@ -242,7 +243,7 @@ object UriUtil {
}
}
} catch (e: Exception) {
- return intent.getParcelableExtra(key)
+ return intent?.getParcelableExtra(key)
}
return null
}
@@ -269,11 +270,15 @@ object UriUtil {
fun contributingUser(context: Context): Boolean {
return (Education.isEducationScreenReclickedPerformed(context)
- || isExternalAppInstalled(context, "com.kunzisoft.keepass.pro", false)
+ || isExternalAppInstalled(
+ context,
+ context.getString(R.string.keepro_app_id),
+ false
+ )
)
}
- private fun isExternalAppInstalled(context: Context, packageName: String, showError: Boolean = true): Boolean {
+ fun isExternalAppInstalled(context: Context, packageName: String, showError: Boolean = true): Boolean {
try {
context.applicationContext.packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES)
Education.setEducationScreenReclickedPerformed(context)
@@ -285,20 +290,35 @@ object UriUtil {
return false
}
- fun openExternalApp(context: Context, packageName: String) {
+ fun openExternalApp(context: Context, packageName: String, sourcesURL: String? = null) {
var launchIntent: Intent? = null
try {
launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)?.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
- } catch (ignored: Exception) {
- }
+ } catch (ignored: Exception) { }
try {
if (launchIntent == null) {
context.startActivity(
Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .setData(Uri.parse("https://play.google.com/store/apps/details?id=$packageName"))
+ .setData(
+ Uri.parse(
+ if (sourcesURL != null
+ && !BuildConfig.CLOSED_STORE
+ ) {
+ sourcesURL
+ } else {
+ context.getString(
+ if (BuildConfig.CLOSED_STORE)
+ R.string.play_store_url
+ else
+ R.string.f_droid_url,
+ packageName
+ )
+ }
+ )
+ )
)
} else {
context.startActivity(launchIntent)
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/HardwareKeySelectionView.kt b/app/src/main/java/com/kunzisoft/keepass/view/HardwareKeySelectionView.kt
new file mode 100644
index 000000000..f2d53d46c
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/view/HardwareKeySelectionView.kt
@@ -0,0 +1,149 @@
+package com.kunzisoft.keepass.view
+
+import android.content.Context
+import android.os.Parcel
+import android.os.Parcelable
+import android.os.Parcelable.Creator
+import android.text.InputType
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Filter
+import androidx.appcompat.widget.AppCompatAutoCompleteTextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.google.android.material.textfield.TextInputLayout
+import com.kunzisoft.keepass.R
+import com.kunzisoft.keepass.hardware.HardwareKey
+import com.kunzisoft.keepass.utils.readEnum
+import com.kunzisoft.keepass.utils.writeEnum
+
+
+class HardwareKeySelectionView @JvmOverloads constructor(context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0)
+ : ConstraintLayout(context, attrs, defStyle) {
+
+ private var mHardwareKey: HardwareKey? = null
+
+ private val hardwareKeyLayout: TextInputLayout
+ private val hardwareKeyCompletion: AppCompatAutoCompleteTextView
+ var selectionListener: ((HardwareKey)-> Unit)? = null
+
+ private val mHardwareKeyAdapter = ArrayAdapterNoFilter(context)
+
+ private class ArrayAdapterNoFilter(context: Context)
+ : ArrayAdapter(context, android.R.layout.simple_list_item_1) {
+ val hardwareKeys = HardwareKey.values()
+
+ override fun getCount(): Int {
+ return hardwareKeys.size
+ }
+
+ override fun getItem(position: Int): String {
+ return hardwareKeys[position].value
+ }
+
+ override fun getItemId(position: Int): Long {
+ // Or just return p0
+ return hardwareKeys[position].hashCode().toLong()
+ }
+
+ override fun getFilter(): Filter {
+ return object : Filter() {
+ override fun performFiltering(p0: CharSequence?): FilterResults {
+ return FilterResults().apply {
+ values = hardwareKeys
+ }
+ }
+
+ override fun publishResults(p0: CharSequence?, p1: FilterResults?) {
+ notifyDataSetChanged()
+ }
+ }
+ }
+ }
+
+ init {
+ val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
+ inflater?.inflate(R.layout.view_hardware_key_selection, this)
+
+ hardwareKeyLayout = findViewById(R.id.input_entry_hardware_key_layout)
+ hardwareKeyCompletion = findViewById(R.id.input_entry_hardware_key_completion)
+
+ hardwareKeyCompletion.isFocusable = false
+ hardwareKeyCompletion.isFocusableInTouchMode = false
+ //hardwareKeyCompletion.isEnabled = false
+ hardwareKeyCompletion.isCursorVisible = false
+ hardwareKeyCompletion.setTextIsSelectable(false)
+ hardwareKeyCompletion.inputType = InputType.TYPE_NULL
+ hardwareKeyCompletion.setAdapter(mHardwareKeyAdapter)
+
+ hardwareKeyCompletion.setOnClickListener {
+ hardwareKeyCompletion.showDropDown()
+ }
+ hardwareKeyCompletion.onItemClickListener =
+ AdapterView.OnItemClickListener { _, _, position, _ ->
+ mHardwareKey = HardwareKey.fromPosition(position)
+ mHardwareKey?.let { hardwareKey ->
+ selectionListener?.invoke(hardwareKey)
+ }
+ }
+ }
+
+ var hardwareKey: HardwareKey?
+ get() {
+ return mHardwareKey
+ }
+ set(value) {
+ mHardwareKey = value
+ hardwareKeyCompletion.setText(value?.toString() ?: "")
+ }
+
+ var error: CharSequence?
+ get() = hardwareKeyLayout.error
+ set(value) {
+ hardwareKeyLayout.error = value
+ }
+
+ override fun onSaveInstanceState(): Parcelable {
+ val superState = super.onSaveInstanceState()
+ val saveState = SavedState(superState)
+ saveState.mHardwareKey = this.mHardwareKey
+ return saveState
+ }
+
+ override fun onRestoreInstanceState(state: Parcelable?) {
+ if (state !is SavedState) {
+ super.onRestoreInstanceState(state)
+ return
+ }
+ super.onRestoreInstanceState(state.superState)
+ this.mHardwareKey = state.mHardwareKey
+ }
+
+ internal class SavedState : BaseSavedState {
+ var mHardwareKey: HardwareKey? = null
+
+ constructor(superState: Parcelable?) : super(superState)
+
+ private constructor(parcel: Parcel) : super(parcel) {
+ mHardwareKey = parcel.readEnum()
+ }
+
+ override fun writeToParcel(out: Parcel, flags: Int) {
+ super.writeToParcel(out, flags)
+ out.writeEnum(mHardwareKey)
+ }
+
+ companion object CREATOR : Creator {
+ override fun createFromParcel(parcel: Parcel): SavedState {
+ return SavedState(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt b/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt
index 75ba32eb4..325e18ba1 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/MainCredentialView.kt
@@ -39,19 +39,24 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
import com.kunzisoft.keepass.model.CredentialStorage
-import com.kunzisoft.keepass.model.MainCredential
+import com.kunzisoft.keepass.database.element.MainCredential
+import com.kunzisoft.keepass.hardware.HardwareKey
class MainCredentialView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0)
: FrameLayout(context, attrs, defStyle) {
- private var passwordTextView: EditText
- private var keyFileSelectionView: KeyFileSelectionView
private var checkboxPasswordView: CompoundButton
+ private var passwordTextView: EditText
private var checkboxKeyFileView: CompoundButton
+ private var keyFileSelectionView: KeyFileSelectionView
+ private var checkboxHardwareView: CompoundButton
+ private var hardwareKeySelectionView: HardwareKeySelectionView
var onPasswordChecked: (CompoundButton.OnCheckedChangeListener)? = null
+ var onKeyFileChecked: (CompoundButton.OnCheckedChangeListener)? = null
+ var onHardwareKeyChecked: (CompoundButton.OnCheckedChangeListener)? = null
var onValidateListener: (() -> Unit)? = null
private var mCredentialStorage: CredentialStorage = CredentialStorage.PASSWORD
@@ -60,15 +65,17 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater?
inflater?.inflate(R.layout.view_main_credentials, this)
+ checkboxPasswordView = findViewById(R.id.password_checkbox)
passwordTextView = findViewById(R.id.password_text_view)
+ checkboxKeyFileView = findViewById(R.id.keyfile_checkbox)
keyFileSelectionView = findViewById(R.id.keyfile_selection)
- checkboxPasswordView = findViewById(R.id.password_checkbox)
- checkboxKeyFileView = findViewById(R.id.keyfile_checkox)
+ checkboxHardwareView = findViewById(R.id.hardware_key_checkbox)
+ hardwareKeySelectionView = findViewById(R.id.hardware_key_selection)
val onEditorActionListener = object : TextView.OnEditorActionListener {
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean {
if (actionId == EditorInfo.IME_ACTION_DONE) {
- onValidateListener?.invoke()
+ validateCredential()
return true
}
return false
@@ -91,7 +98,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
if (keyEvent.action == KeyEvent.ACTION_DOWN
&& keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER
) {
- onValidateListener?.invoke()
+ validateCredential()
handled = true
}
handled
@@ -100,10 +107,30 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
checkboxPasswordView.setOnCheckedChangeListener { view, checked ->
onPasswordChecked?.onCheckedChanged(view, checked)
}
+ checkboxKeyFileView.setOnCheckedChangeListener { view, checked ->
+ if (checked) {
+ if (keyFileSelectionView.uri == null) {
+ checkboxKeyFileView.isChecked = false
+ }
+ }
+ onKeyFileChecked?.onCheckedChanged(view, checked)
+ }
+ checkboxHardwareView.setOnCheckedChangeListener { view, checked ->
+ if (checked) {
+ if (hardwareKeySelectionView.hardwareKey == null) {
+ checkboxHardwareView.isChecked = false
+ }
+ }
+ onHardwareKeyChecked?.onCheckedChanged(view, checked)
+ }
+
+ hardwareKeySelectionView.selectionListener = { _ ->
+ checkboxHardwareView.isChecked = true
+ }
}
- fun setOpenKeyfileClickListener(externalFileHelper: ExternalFileHelper?) {
- keyFileSelectionView.setOpenDocumentClickListener(externalFileHelper)
+ fun validateCredential() {
+ onValidateListener?.invoke()
}
fun populatePasswordTextView(text: String?) {
@@ -118,7 +145,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
}
}
- fun populateKeyFileTextView(uri: Uri?) {
+ fun populateKeyFileView(uri: Uri?) {
if (uri == null || uri.toString().isEmpty()) {
keyFileSelectionView.uri = null
if (checkboxKeyFileView.isChecked)
@@ -130,16 +157,36 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
}
}
+ fun populateHardwareKeyView(hardwareKey: HardwareKey?) {
+ if (hardwareKey == null) {
+ hardwareKeySelectionView.hardwareKey = null
+ if (checkboxHardwareView.isChecked)
+ checkboxHardwareView.isChecked = false
+ } else {
+ hardwareKeySelectionView.hardwareKey = hardwareKey
+ if (!checkboxHardwareView.isChecked)
+ checkboxHardwareView.isChecked = true
+ }
+ }
+
+ fun setOpenKeyfileClickListener(externalFileHelper: ExternalFileHelper?) {
+ keyFileSelectionView.setOpenDocumentClickListener(externalFileHelper)
+ }
+
fun isFill(): Boolean {
- return checkboxPasswordView.isChecked || checkboxKeyFileView.isChecked
+ return checkboxPasswordView.isChecked
+ || (checkboxKeyFileView.isChecked && keyFileSelectionView.uri != null)
+ || (checkboxHardwareView.isChecked && hardwareKeySelectionView.hardwareKey != null)
}
fun getMainCredential(): MainCredential {
return MainCredential().apply {
- this.masterPassword = if (checkboxPasswordView.isChecked)
+ this.password = if (checkboxPasswordView.isChecked)
passwordTextView.text?.toString() else null
this.keyFileUri = if (checkboxKeyFileView.isChecked)
keyFileSelectionView.uri else null
+ this.hardwareKey = if (checkboxHardwareView.isChecked)
+ hardwareKeySelectionView.hardwareKey else null
}
}
@@ -151,7 +198,7 @@ class MainCredentialView @JvmOverloads constructor(context: Context,
// TODO HARDWARE_KEY
return when (mCredentialStorage) {
CredentialStorage.PASSWORD -> checkboxPasswordView.isChecked
- CredentialStorage.KEY_FILE -> checkboxPasswordView.isChecked
+ CredentialStorage.KEY_FILE -> false
CredentialStorage.HARDWARE_KEY -> false
}
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt
index 2c7b4e968..a0f30b977 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt
@@ -68,7 +68,7 @@ class TemplateView @JvmOverloads constructor(context: Context,
setCopyButtonState(TextFieldView.ButtonState.ACTIVATE)
setCopyButtonClickListener { label, value ->
mOnCopyActionClickListener
- ?.invoke(Field(label, ProtectedString(false, value)))
+ ?.invoke(Field(label, ProtectedString(true, value)))
}
} else {
setCopyButtonState(TextFieldView.ButtonState.GONE)
diff --git a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt
index 44182c9ee..f9de51654 100644
--- a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt
@@ -226,8 +226,8 @@ fun View.updateLockPaddingLeft() {
fun Context.showActionErrorIfNeeded(result: ActionRunnable.Result) {
if (!result.isSuccess) {
- result.exception?.errorId?.let { errorId ->
- Toast.makeText(this, errorId, Toast.LENGTH_LONG).show()
+ result.exception?.getLocalizedMessage(resources)?.let { errorMessage ->
+ Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show()
} ?: result.message?.let { message ->
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
@@ -236,8 +236,8 @@ fun Context.showActionErrorIfNeeded(result: ActionRunnable.Result) {
fun CoordinatorLayout.showActionErrorIfNeeded(result: ActionRunnable.Result) {
if (!result.isSuccess) {
- result.exception?.errorId?.let { errorId ->
- Snackbar.make(this, errorId, Snackbar.LENGTH_LONG).asError().show()
+ result.exception?.getLocalizedMessage(resources)?.let { errorMessage ->
+ Snackbar.make(this, errorMessage, Snackbar.LENGTH_LONG).asError().show()
} ?: result.message?.let { message ->
Snackbar.make(this, message, Snackbar.LENGTH_LONG).asError().show()
}
diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt
index f1383f55d..0fafefbc6 100644
--- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt
@@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData
import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.app.database.IOActionTask
+import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil
@@ -72,8 +73,12 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
}
}
- fun addDatabaseFile(databaseUri: Uri, keyFileUri: Uri?) {
- mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(databaseUri, keyFileUri) { databaseFileAdded ->
+ fun addDatabaseFile(databaseUri: Uri, keyFileUri: Uri?, hardwareKey: HardwareKey?) {
+ mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(
+ databaseUri,
+ keyFileUri,
+ hardwareKey
+ ) { databaseFileAdded ->
databaseFileAdded?.let { _ ->
databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
this.databaseFileAction = DatabaseFileAction.ADD
@@ -96,6 +101,7 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
.find { it.databaseUri == databaseFileUpdated.databaseUri }
?.apply {
keyFileUri = databaseFileUpdated.keyFileUri
+ hardwareKey = databaseFileUpdated.hardwareKey
databaseAlias = databaseFileUpdated.databaseAlias
databaseFileExists = databaseFileUpdated.databaseFileExists
databaseLastModified = databaseFileUpdated.databaseLastModified
diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt
index 8608b144a..99a89642d 100644
--- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseViewModel.kt
@@ -87,8 +87,8 @@ class DatabaseViewModel: ViewModel() {
_saveDatabase.value = save
}
- fun mergeDatabase(fixDuplicateUuid: Boolean) {
- _mergeDatabase.value = fixDuplicateUuid
+ fun mergeDatabase(save: Boolean) {
+ _mergeDatabase.value = save
}
fun reloadDatabase(fixDuplicateUuid: Boolean) {
@@ -196,6 +196,8 @@ class DatabaseViewModel: ViewModel() {
data class SuperLong(val oldValue: Long,
val newValue: Long,
val save: Boolean)
+ data class SuperMerge(val fixDuplicateUuid: Boolean,
+ val save: Boolean)
data class SuperCompression(val oldValue: CompressionAlgorithm,
val newValue: CompressionAlgorithm,
val save: Boolean)
diff --git a/app/src/main/res/drawable-hdpi/notification_ic_database_load.png b/app/src/main/res/drawable-hdpi/notification_ic_database_action.png
similarity index 100%
rename from app/src/main/res/drawable-hdpi/notification_ic_database_load.png
rename to app/src/main/res/drawable-hdpi/notification_ic_database_action.png
diff --git a/app/src/main/res/drawable-mdpi/notification_ic_database_load.png b/app/src/main/res/drawable-mdpi/notification_ic_database_action.png
similarity index 100%
rename from app/src/main/res/drawable-mdpi/notification_ic_database_load.png
rename to app/src/main/res/drawable-mdpi/notification_ic_database_action.png
diff --git a/app/src/main/res/drawable-xhdpi/notification_ic_database_load.png b/app/src/main/res/drawable-xhdpi/notification_ic_database_action.png
similarity index 100%
rename from app/src/main/res/drawable-xhdpi/notification_ic_database_load.png
rename to app/src/main/res/drawable-xhdpi/notification_ic_database_action.png
diff --git a/app/src/main/res/drawable-xxhdpi/notification_ic_database_load.png b/app/src/main/res/drawable-xxhdpi/notification_ic_database_action.png
similarity index 100%
rename from app/src/main/res/drawable-xxhdpi/notification_ic_database_load.png
rename to app/src/main/res/drawable-xxhdpi/notification_ic_database_action.png
diff --git a/app/src/main/res/drawable-xxxhdpi/notification_ic_database_load.png b/app/src/main/res/drawable-xxxhdpi/notification_ic_database_action.png
similarity index 100%
rename from app/src/main/res/drawable-xxxhdpi/notification_ic_database_load.png
rename to app/src/main/res/drawable-xxxhdpi/notification_ic_database_action.png
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
index 68390273c..d6eab3c13 100644
--- a/app/src/main/res/layout/activity_about.xml
+++ b/app/src/main/res/layout/activity_about.xml
@@ -34,7 +34,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/toolbar"
- app:layout_constraintBottom_toBottomOf="parent">
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner">
+
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/layout/activity_entry.xml b/app/src/main/res/layout/activity_entry.xml
index 96c2b2937..f7e355eeb 100644
--- a/app/src/main/res/layout/activity_entry.xml
+++ b/app/src/main/res/layout/activity_entry.xml
@@ -17,165 +17,174 @@
You should have received a copy of the GNU General Public License
along with KeePassDX. If not, see .
-->
-
-
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner"
+ app:layout_constraintTop_toTopOf="parent">
-
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:paddingTop="12dp"
+ android:paddingStart="5dp"
+ android:paddingLeft="5dp"
+ android:paddingEnd="5dp"
+ android:paddingRight="5dp"
+ android:layout_gravity="center"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+ android:orientation="horizontal"
+ app:layout_constraintTop_toBottomOf="@+id/history_container"/>
+
+
+
-
-
+
-
+
+
+
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:indeterminate="true" />
+
-
-
-
-
-
-
-
+ android:layout_gravity="start|bottom" />
-
+
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/layout/activity_entry_edit.xml b/app/src/main/res/layout/activity_entry_edit.xml
index 67ba1771c..14ed20bd8 100644
--- a/app/src/main/res/layout/activity_entry_edit.xml
+++ b/app/src/main/res/layout/activity_entry_edit.xml
@@ -84,7 +84,7 @@
android:layout_height="?attr/actionBarSize"
android:theme="?attr/toolbarActionAppearance"
android:layout_gravity="bottom"
- app:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner" />
@@ -105,7 +105,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner"/>
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/layout/activity_file_selection.xml b/app/src/main/res/layout/activity_file_selection.xml
index f1d42d78f..96448a944 100644
--- a/app/src/main/res/layout/activity_file_selection.xml
+++ b/app/src/main/res/layout/activity_file_selection.xml
@@ -146,7 +146,7 @@
android:id="@+id/file_selection_buttons_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/layout/activity_group.xml b/app/src/main/res/layout/activity_group.xml
index cdeed30e1..9701e0534 100644
--- a/app/src/main/res/layout/activity_group.xml
+++ b/app/src/main/res/layout/activity_group.xml
@@ -27,7 +27,7 @@
android:filterTouchesWhenObscured="true"
android:fitsSystemWindows="true">
-
@@ -36,16 +36,17 @@
android:id="@+id/special_mode_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:theme="?attr/toolbarSpecialAppearance" />
+ android:theme="?attr/toolbarSpecialAppearance"
+ app:layout_constraintTop_toTopOf="parent" />
+ android:theme="?attr/toolbarAppearance"
+ android:title="@string/app_name"
+ app:layout_constraintTop_toBottomOf="@+id/special_mode_view">
-
+
+
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@+id/toolbar_action"
+ app:layout_constraintTop_toBottomOf="@+id/toolbar">
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner" />
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner" />
-
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_icon_picker.xml b/app/src/main/res/layout/activity_icon_picker.xml
index fee7d16f7..a56c04c8d 100644
--- a/app/src/main/res/layout/activity_icon_picker.xml
+++ b/app/src/main/res/layout/activity_icon_picker.xml
@@ -43,7 +43,7 @@
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:theme="?attr/toolbarActionAppearance"
- app:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner" />
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/toolbar" />
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner"
+ app:layout_constraintStart_toStartOf="parent" />
+
+
diff --git a/app/src/main/res/layout/activity_image_viewer.xml b/app/src/main/res/layout/activity_image_viewer.xml
index a600a7c0a..d9eb93679 100644
--- a/app/src/main/res/layout/activity_image_viewer.xml
+++ b/app/src/main/res/layout/activity_image_viewer.xml
@@ -14,7 +14,7 @@
-
\ No newline at end of file
+
+
+
diff --git a/app/src/main/res/layout/activity_key_generator.xml b/app/src/main/res/layout/activity_key_generator.xml
index 30f598dab..2ac63a461 100644
--- a/app/src/main/res/layout/activity_key_generator.xml
+++ b/app/src/main/res/layout/activity_key_generator.xml
@@ -43,7 +43,7 @@
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:theme="?attr/toolbarActionAppearance"
- app:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner" />
@@ -64,5 +64,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner" />
+
+
diff --git a/app/src/main/res/layout/activity_main_credential.xml b/app/src/main/res/layout/activity_main_credential.xml
index 84ae78ce4..4e968b227 100644
--- a/app/src/main/res/layout/activity_main_credential.xml
+++ b/app/src/main/res/layout/activity_main_credential.xml
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with KeePassDX. If not, see .
-->
-
+ app:layout_constraintTop_toBottomOf="@+id/special_mode_view"
+ app:layout_constraintBottom_toTopOf="@+id/activity_password_footer">
+ app:layout_constraintBottom_toTopOf="@+id/screenshot_mode_banner">
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/layout/activity_toolbar.xml b/app/src/main/res/layout/activity_toolbar.xml
index fff49771c..b11a3bac7 100644
--- a/app/src/main/res/layout/activity_toolbar.xml
+++ b/app/src/main/res/layout/activity_toolbar.xml
@@ -17,15 +17,22 @@
You should have received a copy of the GNU General Public License
along with KeePassDX. If not, see .
-->
-
+
+
+
+
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/layout/fragment_progress.xml b/app/src/main/res/layout/fragment_progress.xml
index b3f79e265..6edd632d2 100644
--- a/app/src/main/res/layout/fragment_progress.xml
+++ b/app/src/main/res/layout/fragment_progress.xml
@@ -58,6 +58,16 @@
android:layout_marginEnd="20dp"
style="@style/KeepassDXStyle.TextAppearance.Warning"/>
+
+
@@ -126,9 +127,41 @@
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@+id/keyfile_checkox"
+ app:layout_constraintStart_toEndOf="@+id/keyfile_checkbox"
app:layout_constraintEnd_toEndOf="parent" />
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_attachment.xml b/app/src/main/res/layout/item_attachment.xml
index c2c15997e..5dbb9e209 100644
--- a/app/src/main/res/layout/item_attachment.xml
+++ b/app/src/main/res/layout/item_attachment.xml
@@ -129,6 +129,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:clickable="false"
android:contentDescription="@string/download"
android:src="@drawable/ic_file_stream_white_24dp"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/app/src/main/res/layout/view_hardware_key_selection.xml b/app/src/main/res/layout/view_hardware_key_selection.xml
new file mode 100644
index 000000000..09ac78e9b
--- /dev/null
+++ b/app/src/main/res/layout/view_hardware_key_selection.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_main_credentials.xml b/app/src/main/res/layout/view_main_credentials.xml
index 2d4ec549d..155268563 100644
--- a/app/src/main/res/layout/view_main_credentials.xml
+++ b/app/src/main/res/layout/view_main_credentials.xml
@@ -63,7 +63,7 @@
android:layout_height="wrap_content">
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/view_screenshot_mode_banner.xml b/app/src/main/res/layout/view_screenshot_mode_banner.xml
new file mode 100644
index 000000000..1c3d35ef7
--- /dev/null
+++ b/app/src/main/res/layout/view_screenshot_mode_banner.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index 6b78462d6..8fde45638 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -2,4 +2,5 @@
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index eca70cfe5..b070c763d 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -2,4 +2,5 @@
+
\ 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 e52747a92..83eee26c9 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -105,7 +105,7 @@
نسخ حقل
تأمين قاعدة البيانات
أرسل انطباعاتك
- التنفيذ لمُدير كلمات المرور «كي باس» على نظام أندرويد
+ \"KeePassDX\" هو تطبيق أندرويد لمدير كلمات المرور كي باس \"KeePass\"
أضف مدخل
تحرير مدخل
وظيفة اشتقاق المفتاح
@@ -118,8 +118,8 @@
اختر لنسخ %1$s إلى الحافظة
يجلب مفتاح قاعدة البيانات…
استخدامها كقاعدة بيانات افتراضية
- KeePassDX © %1$d كونزيسوفت مفتوح المصدر و بدون اعلانات.
-\n يوزع كما هو، بدون ضمان, تحت ترخيص GPLv3.
+ KeePassDX © %1$d كونزيسوفت <strong>مفتوح المصدر</strong> و <strong>بدون اعلانات</strong>.
+\n يوزع كما هو، بدون ضمان, تحت ترخيص <strong>GPLv3</strong>.
نُفذ إليه
تنتهي صلاحيته في
ملف المفتاح
@@ -520,4 +520,6 @@
الإشعارات
لا تستخدم أي محتوى معمى لاستخدام فك القفل المتقدم
انتهاء صلاحية فك القفل المتقدم
+ إخفاء الإدخالات منتهية الصلاحية
+ خانة إختيار مفتاح الجهاز
\ No newline at end of file
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index 855687db0..56c888497 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -93,4 +93,276 @@
বড় হাতের
সতর্কতা
ক্লিপবোর্ডে সংরক্ষণ সময়কাল (যদি তোমার যন্ত্র সনর্থন করে)
+ খোলা ফাইল
+ নোড শিশু
+ নোড যোগ করুন
+ এন্ট্রি যোগ করুন
+ গ্রুপ যোগ করুন
+ আইটেম যোগ করুন
+ ফাইল তথ্য
+ শংসাপত্রের তথ্য
+ এককালীন পাসওয়ার্ড তথ্য
+ পাসওয়ার্ড চেকবক্স
+ কীফাইল চেকবক্স
+ টগল পাসওয়ার্ড দৃশ্যমানতা পুনরাবৃত্তি করুন
+ পরিবর্তন বাতিল\?
+ নেভিগেশন ড্রয়ার খোলা
+ নেভিগেশন ড্রয়ার বন্ধ
+ পাসওয়ার্ড জেনারেটর
+ পাসওয়ার্ড দৈর্ঘ্য
+ পাসফ্রেজ শব্দ সংখ্যা
+ ক্ষেত্র যোগ করুন
+ সংযুক্তি যোগ
+ ক্ষেত্র সরান
+ ডাটাবেস বিষয়বস্তু ডিক্রিপ্ট করা হচ্ছে…
+ ডিফল্ট ডাটাবেস হিসাবে ব্যবহার করুন
+ পাসওয়ার্ড নিশ্চিত করুন
+ অনুসন্ধানযোগ্য
+ উত্তরাধিকার
+ অটো-টাইপ
+ অটো-টাইপ ক্রম
+ এন্ট্রি ডেটা খুঁজে পাওয়া যায়নি.
+ ট্যাগ
+ কাস্টম ডেটা
+ সময়কাল (সেকেন্ড)
+ অনুসন্ধান ফিল্টার
+ বর্তমান গ্রুপ
+ কেস সংবেদনশীল
+ নিয়মিত অভিব্যক্তি
+ ডেবিট/ক্রেডিট কার্ড
+ ধারক
+ নাম
+ সংখ্যা
+ সিভিভি
+ পিন
+ ঘটনার কেন্দ্রবিন্দু
+ প্রদান এর তারিখ
+ ইমেইল
+ ইমেইল ঠিকানা
+ ওয়াইফাই
+ SSID
+ টাইপ
+ টোকেন
+ পাবলিক কী
+ ব্যক্তিগত কী
+ বীজ
+ হিসাব
+ ব্যাংক
+ ব্যাংকের নাম
+ সুইফট/বিআইসি
+ স্ট্যান্ডার্ড
+ টেমপ্লেট
+ সংস্করণ
+ আর্কফোর স্ট্রিম সাইফার সমর্থিত নয়।
+ KeePassDX-এ এই URI পরিচালনা করা যায়নি।
+ ফাইল তৈরি করা যায়নি
+ ডাটাবেস পড়া যায়নি.
+ পথ সঠিক কিনা নিশ্চিত করুন।
+ অবৈধ OTP গোপন.
+ একটি নাম লিখুন।
+ কমপক্ষে একটি পাসওয়ার্ড জেনারেশন টাইপ নির্বাচন করতে হবে।
+ অন্তত একটি শংসাপত্র সেট করা আবশ্যক.
+ প্রতিটি স্ট্রিং একটি ক্ষেত্রের নাম থাকতে হবে.
+ এই লেবেলটি ইতিমধ্যেই বিদ্যমান।
+ \"ট্রান্সফরমেশন রাউন্ড\" খুব বেশি। 2147483648 এ সেট করা হচ্ছে।
+ আপনি এখানে একটি এন্ট্রি সরাতে পারবেন না.
+ আপনি এখানে একটি এন্ট্রি কপি করতে পারবেন না.
+ আপনি এখানে একটি গ্রুপ অনুলিপি করতে পারবেন না.
+ ডাটাবেস ফাইল তৈরি করতে অক্ষম।
+ এই পাসওয়ার্ড এবং কীফাইল দিয়ে ডাটাবেস তৈরি করতে অক্ষম৷
+ আপনি এখানে একটি গ্রুপ সরাতে পারবেন না.
+ বিদ্যমান OTP প্রকারটি এই ফর্ম দ্বারা স্বীকৃত নয়, এর বৈধতা আর সঠিকভাবে টোকেন তৈরি করতে পারে না।
+ এই পাঠ্যটি অনুরোধ করা আইটেমের সাথে মেলে না।
+ নথি ব্যবস্থাপক
+ ফাইল খুঁজে পাই নি. আপনার ফাইল ব্রাউজার থেকে এটি পুনরায় খোলার চেষ্টা করুন।
+ পাসওয়ার্ড জেনারেট
+ পাসওয়ার্ড নিশ্চিত করুন
+ শংসাপত্র পড়া যায়নি.
+ ভুল অ্যালগরিদম।
+ একই UUID সহ %1$s %2$s ইতিমধ্যেই বিদ্যমান।
+ পাসফ্রেজ
+ কী ফাইলটি খালি।
+ পাসওয়ার্ড লুকান
+ উপাদান তালিকায় পাঠ্যের আকার
+ ডাটাবেস তৈরি করা হচ্ছে…
+ ডাটাবেস লোড হচ্ছে…
+ %1$s এর কপি
+ ফরম পূরণ
+ উন্নত আনলকিং
+ ডাটাবেস সেটিংস
+ নিরাপত্তা বিন্যাস
+ মাস্টার কী সেটিংস
+ ইতিহাস মুছুন
+ বাহ্যিক আইকন
+ কোন অনুসন্ধান ফলাফল নেই
+ এই URL খুলতে একটি ওয়েব ব্রাউজার ইনস্টল করুন.
+ নতুন ডাটাবেস তৈরি করুন
+ দ্রুত অনুসন্ধান
+ একটি ডাটাবেস খোলার সময় একটি অনুসন্ধান অনুরোধ
+ সাবডোমেন অনুসন্ধান
+ সাবডোমেন সীমাবদ্ধতা সহ ওয়েব ডোমেন অনুসন্ধান করুন
+ নতুন ডাটাবেস তৈরি করা হচ্ছে…
+ বিদ্যমান ডাটাবেস খুলুন
+ আপনার ফাইল ম্যানেজারের উপর নির্ভর করে, KeePassDX আপনার স্টোরেজে লেখার অনুমতি নাও দিতে পারে।
+ ডাটাবেসে ডুপ্লিকেট UUID আছে।
+ সদৃশগুলি চালিয়ে যাওয়ার জন্য নতুন UUID তৈরি করে সমস্যার সমাধান করবেন\?
+ অনুসন্ধান মোড
+ সেভ মোড
+ নির্বাচন মোড
+ রেজিস্ট্রেশন মোড
+ কী ফাইলগুলি কোথায় সংরক্ষণ করা হয় তা ট্র্যাক রাখে
+ সাম্প্রতিক ফাইল দেখান
+ সাম্প্রতিক ডাটাবেসের অবস্থান দেখান
+ ভাঙ্গা ডাটাবেস লিঙ্ক লুকান
+ সাম্প্রতিক ডেটাবেসের তালিকায় ভাঙা লিঙ্কগুলি লুকান
+ ডাটাবেস অবস্থান মনে রাখবেন
+ ডাটাবেস কোথায় সংরক্ষণ করা হয় তা ট্র্যাক রাখে
+ কীফাইল অবস্থান মনে রাখবেন
+ অ্যাপের বৈশিষ্ট্য আমদানি করা হয়েছে
+ অ্যাপের বৈশিষ্ট্য রপ্তানি করা হয়েছে
+ কী ডেরিভেশন ফাংশন দ্বারা ব্যবহৃত মেমরির পরিমাণ।
+ মেমরি ব্যবহার
+ সর্বনিম্ন প্রথম ↓
+ আগে দল
+ নীচে রিসাইকেল বিন
+ প্রাকৃতিক নিয়ম
+ অসমর্থিত ডাটাবেস সংস্করণ।
+ ডাটাবেস পরিবর্তনগুলি সংরক্ষণ করতে ফাইল লেখার অ্যাক্সেস মঞ্জুর করুন
+ ফাইল ম্যানেজার কর্তৃক প্রত্যাহার করা ফাইলটিতে অ্যাক্সেস
+ ডাটাবেস ফাইলে টেক্সট এনকোডিং ফরম্যাটের বাইরে পাসওয়ার্ড অক্ষর এড়িয়ে চলুন (অপরিচিত অক্ষরগুলি একই অক্ষরে রূপান্তরিত হয়)।
+ পাসওয়ার্ড আনলকিং সুরক্ষা ছাড়াই চালিয়ে যেতে চান\?
+ স্থায়ীভাবে নির্বাচিত নোড মুছে ফেলবেন\?
+ এনক্রিপশন কী ছাড়াই চালিয়ে যেতে চান\?
+ রিসাইকেল বিন থেকে স্থায়ীভাবে সব নোড মুছে ফেলবেন\?
+ একটি KeePass ডাটাবেসে শুধুমাত্র ছোট ইউটিলিটি ফাইল (যেমন PGP কী ফাইল) থাকার কথা।
+\n
+\nআপনার ডাটাবেস খুব বড় হতে পারে এবং এই আপলোডের সাথে কর্মক্ষমতা হ্রাস করতে পারে।
+ এই ফাইলটি আপলোড করা বিদ্যমান ফাইলটিকে প্রতিস্থাপন করবে৷
+ যাইহোক ফাইল যোগ করুন\?
+ আপনার ডাটাবেস ফাইলে থাকা তথ্য অ্যাপের বাইরে পরিবর্তন করা হয়েছে।
+ ডাটাবেস পুনরায় লোড করা স্থানীয়ভাবে পরিবর্তিত ডেটা মুছে ফেলবে।
+ ফাইল ম্যানেজার দ্বারা প্রত্যাহার করা ফাইলটিতে অ্যাক্সেস, ডাটাবেসটি বন্ধ করুন এবং এটির অবস্থান থেকে পুনরায় খুলুন।
+ কোনো বায়োমেট্রিক বা ডিভাইস শংসাপত্র নথিভুক্ত করা হয় না.
+ নেভিগেশন হেডার
+ ডেটাবেস এনক্রিপশন অ্যালগরিদম সমস্ত ডেটার জন্য ব্যবহৃত হয়
+ <strong>কোন ব্যবহারকারীর ডেটা পুনরুদ্ধার করা হয় না</strong>, এই অ্যাপ্লিকেশনটি কোনো সার্ভারের সাথে সংযোগ করে না, শুধুমাত্র স্থানীয়ভাবে কাজ করে এবং ব্যবহারকারীদের গোপনীয়তাকে সম্পূর্ণভাবে সম্মান করে।
+ ক্রিপ্টোকারেন্সি ওয়ালেট
+ IBAN
+ সুরক্ষিত নোট
+ সদস্যপদ
+ এই শব্দটি সংরক্ষিত এবং ব্যবহার করা যাবে না।
+ একটি কীফাইল নির্বাচন করুন।
+ একটি হার্ডওয়্যার কী নির্বাচন করুন।
+ XML বিকৃত।
+ ডাটাবেস লোড করা যায়নি।
+ পাসওয়ার্ড মেলে না.
+ \"দৈর্ঘ্য\" ক্ষেত্রে একটি ধনাত্মক পূর্ণসংখ্যা লিখুন।
+ অটোফিল পরিষেবা সক্ষম করা যায়নি৷
+ শুধুমাত্র পঠনযোগ্য ডাটাবেসে একটি নতুন আইটেম সংরক্ষণ করা অনুমোদিত নয়
+ ডাটাবেস URI পুনরুদ্ধার করা যাবে না।
+ সঠিকভাবে তালিকা পুনর্নির্মাণ করতে অক্ষম.
+ ফাইল ডেটা ইতিমধ্যেই বিদ্যমান।
+ প্রতিক্রিয়া ইতিমধ্যে প্রদান করা হয়েছে.
+ %1$s এর জন্য ড্রাইভার প্রয়োজন৷
+ একটি ডাটাবেস V1 থেকে মার্জ করতে অক্ষম৷
+ ডাটাবেস অবস্থান অজানা, ডাটাবেস কর্ম সঞ্চালিত করা যাবে না.
+ হার্ডওয়্যার কী সমর্থিত নয়।
+ কী খালি হতে পারে না।
+ পাসওয়ার্ড তৈরি করা হয়েছে
+ দলের নাম
+ আইকনের নাম
+ ডাটাবেস বিন্যাস চিনতে পারেনি.
+ পাসওয়ার্ড রঙ করুন
+ টাইপ অনুসারে পাসওয়ার্ড অক্ষর রঙ করুন
+ এন্ট্রি তালিকায় ব্যবহারকারীর নাম প্রদর্শন করুন
+ এন্ট্রি সংখ্যা দেখান
+ হার্ডওয়্যার কী মনে রাখবেন
+ ব্যবহৃত হার্ডওয়্যার কী ট্র্যাক রাখে
+ অ্যাপ বৈশিষ্ট্য আমদানি করুন
+ অ্যাপ্লিকেশন বৈশিষ্ট্য আমদানি করতে একটি ফাইল নির্বাচন করুন
+ এনক্রিপশন অ্যালগরিদমের জন্য কী তৈরি করতে, মাস্টার কীটি এলোমেলোভাবে সল্টেড কী ডেরিভেশন ফাংশন ব্যবহার করে রূপান্তরিত হয়।
+ রূপান্তর রাউন্ড
+ অ্যাপটি মেরে ফেলবেন না…
+ ছাঁকনি
+ যাইহোক এই ডেটা সরাবেন\?
+ এটি একটি খালি কীফাইল যোগ করার সুপারিশ করা হয় না.
+ আপনি অ্যাপটিকে সঠিক অ্যালার্ম ব্যবহার করার অনুমতি দেননি৷ ফলস্বরূপ, টাইমারের প্রয়োজনীয় বৈশিষ্ট্যগুলি সঠিক সময়ের সাথে করা হবে না।
+ অনুমতি
+ সংস্করণ %1$s
+ %1$s তৈরি করুন
+ বায়োমেট্রিক নিরাপত্তা আপডেট প্রয়োজন.
+ OTP প্রকার
+ এন্ট্রি আইকন
+ ডাটাবেসের রঙ
+ এন্ট্রি ফোরগ্রাউন্ড রঙ
+ এন্ট্রি পটভূমির রঙ
+ ক্ষেত্রগুলি বন্ধ করুন
+ মেয়াদোত্তীর্ণ
+ ক্লিপবোর্ডে অনুলিপি করতে নির্বাচন করুন
+ এককালীন পাসওয়ার্ড সেট আপ করুন
+ KeePassDX © %1$d Kunzisoft হল <strong>ওপেন সোর্স</strong> এবং <strong>বিজ্ঞাপন ছাড়াই</strong>।
+\nএটি <strong>GPLv3</strong> লাইসেন্সের অধীনে, কোনো ওয়ারেন্টি ছাড়াই প্রদান করা হয়।
+ <strong>আমাদের স্বাধীনতা বজায় রাখতে</strong>, <strong>বাগগুলি ঠিক করতে</strong>, <strong>বৈশিষ্ট্য যোগ করুন</strong> এবং <strong>সর্বদা সক্রিয় থাকার জন্য</strong>, আমরা আপনার উপর নির্ভর করি <strong>অবদান</strong>।
+ হার্ডওয়্যার কী
+ পরিচয় পত্র
+ আপনার সম্পূর্ণ ডাটাবেস লোড করার জন্য কোন মেমরি নেই।
+ চাবি লোড করা যায়নি. KDF \"মেমরি ব্যবহার\" কম করার চেষ্টা করুন।
+ ক্ষেত্রের নাম ইতিমধ্যেই বিদ্যমান।
+ আপনি যে ফাইলটি আপলোড করার চেষ্টা করছেন তা খুব বড়।
+ চ্যালেঞ্জ থেকে সাড়া পাওয়া যাচ্ছে না।
+ তালিকা আইটেম আকার
+ ব্যবহারকারী দ্বারা বাতিল.
+ OTP টোকেন দেখান
+ ক্ষেত্র নাম
+ ক্ষেত্রের মান
+ দূষিত ফাইল।
+ UUID দেখান
+ ডিফল্টরূপে মাস্ক পাসওয়ার্ড (***)
+ ব্যবহারকারীর নাম দেখান
+ একটি এন্ট্রি বা একটি গ্রুপের সাথে সংযুক্ত UUID প্রদর্শন করে
+ ডেটা পুনরায় লোড করুন
+ এন্ট্রির তালিকায় OTP টোকেন প্রদর্শন করে
+ এতে একটি অনুলিপি সংরক্ষণ করুন…
+ উন্নত আনলক কী মুছুন
+ একটি গ্রুপে এন্ট্রির সংখ্যা প্রদর্শন করে
+ মাস্টার কী পরিবর্তন করুন
+ অ্যাপ সেটিংস
+ তথ্য সংরক্ষণ
+ ডেটা মার্জ করুন
+ থেকে মার্জ করুন…
+ পাসওয়ার্ড দেখাও
+ পাসওয়ার্ড লুকান
+ ডাটাবেস লক করুন
+ URL-এ যান
+ রিসাইকেল বিন খালি করুন
+ ইতিহাস পুনরুদ্ধার করুন
+ অতিরিক্ত এনক্রিপশন রাউন্ডগুলি ব্রুট ফোর্স অ্যাটাকগুলির বিরুদ্ধে উচ্চ সুরক্ষা প্রদান করে, তবে এটি সত্যিই লোড এবং সংরক্ষণকে ধীর করে দিতে পারে।
+ অ্যাপ্লিকেশন বৈশিষ্ট্য রপ্তানি সময় ত্রুটি
+ কমান্ড কার্যকর করা হচ্ছে…
+ অ্যাপের বৈশিষ্ট্য রপ্তানি করুন
+ অ্যাপের বৈশিষ্ট্য রপ্তানি করতে একটি ফাইল তৈরি করুন
+ অ্যাপ সেটিংস পরিচালনা করতে KeePassDX বৈশিষ্ট্য
+ অ্যাপ বৈশিষ্ট্য আমদানির সময় ত্রুটি
+ একটি ডাটাবেস ইতিমধ্যেই খোলা আছে, নতুনটি খুলতে প্রথমে এটি বন্ধ করুন
+ কীফাইলের বিষয়বস্তু কখনই পরিবর্তন করা উচিত নয় এবং সর্বোত্তম ক্ষেত্রে, এলোমেলোভাবে উৎপন্ন ডেটা থাকা উচিত।
+ আনলিঙ্ক করা ডেটা মুছে দিলে আপনার ডাটাবেসের আকার কমে যেতে পারে কিন্তু KeePass প্লাগইনগুলির জন্য ব্যবহৃত ডেটাও মুছে যেতে পারে।
+ ফাইলটির হ্যাশ নিশ্চিত নয় কারণ অ্যান্ড্রয়েড ফ্লাইতে তার ডেটা পরিবর্তন করতে পারে। সঠিক অখণ্ডতার জন্য ফাইল এক্সটেনশন .bin এ পরিবর্তন করুন।
+ ডেটা মার্জ করুন, ডাটাবেস সংরক্ষণ করে বাহ্যিক পরিবর্তনগুলি ওভাররাইট করুন বা সর্বশেষ পরিবর্তনগুলির সাথে এটি পুনরায় লোড করুন।
+ হার্ডওয়্যার কী চেকবক্স
+ ডাটাবেস কী পুনরুদ্ধার করা হচ্ছে…
+ চ্যালেঞ্জ অনুরোধের জন্য অপেক্ষা করা হচ্ছে…
+ চ্যালেঞ্জের প্রতিক্রিয়ার জন্য অপেক্ষা করা হচ্ছে…
+ টেমপ্লেট
+ সংরক্ষণ
+ ফাইল ডেটা সরানোর সময় একটি ত্রুটি ঘটেছে৷
+ ডাটাবেসে একটি ক্রিয়া সম্পাদন করার সময় একটি ত্রুটি ঘটেছে৷
+ চ্যালেঞ্জ ইতিমধ্যে অনুরোধ করা হয়েছে
+ গোপন কী অবশ্যই বেস 32 ফর্ম্যাটে হতে হবে।
+ কাউন্টার অবশ্যই %1$d এবং %2$d এর মধ্যে হতে হবে।
+ সময়কাল %1$d এবং %2$d সেকেন্ডের মধ্যে হতে হবে।
+ টোকেনে %1$d থেকে %2$d সংখ্যা থাকতে হবে।
+ ফাইল ডেটা আপলোড করার সময় একটি ত্রুটি ঘটেছে৷
+ ডাটাবেস সংরক্ষণ করা যায়নি.
+ কী ডেরিভেশন ফাংশন দ্বারা ব্যবহৃত সমান্তরালতার ডিগ্রি (যেমন থ্রেডের সংখ্যা)।
+ ডাটাবেস সংরক্ষণ করা হচ্ছে…
\ No newline at end of file
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index 8bfde1140..1fc86dbbb 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -300,15 +300,15 @@
Protecció contra escriptura de la base de dades
Ajudeu a augmentar l’estabilitat i la seguretat i a crear més funcionalitats.
Participació
- En comprar la versió professional,
- En col·laborar-hi,
+ En comprar la versió <strong>professional</strong>,
+ En <strong>col·laborar-hi</strong>,
Casella del fitxer de la clau
Casella de la contrasenya
Informació de la contrasenya d’un sol ús
Informació de les dades d’accés
Afegeix un element
Bloca la base de dades
- Aquest estil visual és disponible gràcies a la vostra generositat.
+ Aquest <strong>estil visual</strong> és disponible gràcies a la vostra generositat.
No us oblideu de mantenir l’aplicació actualitzada instal·lant les versions noves.
Estàndard
Mostra l’UUID
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 10c704587..de3bfd747 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -37,9 +37,9 @@
Časový limit schránky
Doba uchování ve schránce (pokud je zařízením podporována)
Vyberte zkopírovat %1$s do schránky
- Načítání klíč databáze…
+ Načítám klíč databáze…
Databáze
- Rozšifrovávání obsahu databáze…
+ Dešifruji obsah databáze…
Použít jako výchozí databázi
Číslice
Otevřít existující databázi
@@ -67,8 +67,8 @@
Nedostatek paměti pro načtení celé databáze.
Je třeba zvolit alespoň jeden způsob vytváření hesla.
Zadání hesla se neshodují.
- Příliš vysoký „Počet průchodů“. Nastavuje se na 2147483648.
- Je třeba, aby každý řetězec měl název kolonky.
+ \"Transformační kola\" jsou příliš vysoká. Nastavuji na 2147483648.
+ Každý řetězec musí mít název kolonky.
Do kolonky „Délka“ zadejte celé kladné číslo.
Název kolonky
Hodnota kolonky
@@ -88,7 +88,7 @@
Délka
Velikost položek seznamu
Velikost textu v seznamu prvků
- Načítání databáze…
+ Načítám databázi…
Malá písmena
Skrýt hesla
Ve výchozím stavu zobrazit (***) místo hesla
@@ -109,7 +109,7 @@
Nikdy
Žádné výsledky hledání
Pro otevření tohoto URL nainstalujte webový prohlížeč.
- Vytváření nové databáze…
+ Zakládám novou databázi…
Pracuji…
Ochrana
Ke změně v databáze potřebuje KeePassDX oprávnění pro zápis.
@@ -117,7 +117,7 @@
Kořen
Transformační průchody
Vyšší počet šifrovacích průchodů zvýší odolnost proti útoku zkoušením všech možných hesel, ale může výrazně zpomalit načítání a ukládání.
- Ukládání databáze…
+ Ukládám databázi…
Mezera
Přirozené řazení
Speciální
@@ -141,7 +141,7 @@
Povolit
Databázi se nepodařilo načíst.
Klíč se nepodařilo načíst, zkuste snížit \"využití paměti\" pro KDF.
- Službu automatického vyplňování se nepodařilo zapnout.
+ Službu samovyplnění se nepodařilo zapnout.
Soubor nenalezen. Zkuste jej otevřít ze správce souborů.
Zobrazit uživatelská jména
Zobrazí uživatelská jména v seznamech záznamů
@@ -154,7 +154,7 @@
Chráněno před zápisem
Čtení a zápis
Chráněno před zápisem
- Algoritmus šifrování databáze použit pro všechna data.
+ Algoritmus šifrování databáze použit pro všechna data
Klíč pro šifrovací algoritmus je vytvořen transformací hlavního klíče pomocí funkce odvození klíče s náhodně přidanou složkou, tzv. solí.
Využití paměti
Množství paměti použitých funkcí pro odvození klíče.
@@ -171,18 +171,18 @@
Přístup
Varování
Nepoužívejte v hesle pro databázový soubor znaky mimo znaky kódování textu (nerozpoznané znaky budou konvertovány na stejné písmeno).
- Pokračovat bez ochrany odemknutí heslem\?
+ Pokračovat bez ochrany heslem\?
Pokračovat bez šifrovacího klíče\?
Šifrované heslo uloženo
Tato databáze zatím nemá uložené heslo.
Historie
Vzhled
Obecné
- Automatické vyplnění
- KeePassDX automatické vyplňování formulářů
+ Samovyplnění
+ KeePassDX samovyplnění formulářů
Přihlásit se s KeePassDX
- Nastavit výchozí službu automatického vyplňování
- Povolit rychlé automatické vyplňování formulářů v ostatních aplikacích
+ Nastavit výchozí službu samovyplnění
+ Zapnout samovyplnění formulářů za účelem rychlého vyplnění v ostatních aplikacích
Délka generovaného hesla
Nastavení výchozí délky generovaných hesel
Znaky hesla
@@ -207,7 +207,7 @@
Přiřadit hlavní klíč
Založit novou databázi
Využití koše
- Před smazáním přesune vybrané položky do skupiny s názvem „Koš“
+ Před smazáním přesune vybrané položky do skupiny s názvem \"Koš\"
Písmo kolonek
Čitelnost znaků v kolonkách můžete přizpůsobit změnou písma
Důvěřovat schránce
@@ -223,7 +223,7 @@
Klávesnice Magikeyboard
Aktivovat vlastní klávesnici, která snadno vyplní hesla a další položky identity
Umožnit bez hlavního klíče
- Povolit klepnutí na „Otevřít“, i když není vybráno žádné heslo
+ Povolit klepnutí na \"Otevřít\", i když není vybráno žádné heslo
Chráněno před zápisem
Ve výchozím stavu otevřít databázi pouze pro čtení
Vzdělávací nápovědy
@@ -251,8 +251,8 @@
Ochraňte svou databázi před zápisem
Změnit režim otevírání pro dané sezení.
\n
-\nV režimu „pouze pro čtení“ zabráníte nechtěným změnám v databázi.
-\nV režimu „umožněné změny“ je možné přidávat, mazat nebo měnit všechny prvky podle libosti.
+\nV režimu \"pouze pro čtení\" zabráníte nechtěným změnám v databázi.
+\nV režimu \"zápisu\" je možné přidávat, mazat nebo měnit všechny prvky podle libosti.
Zkopírovat kolonku
Zkopírované kolonky lze vkládat podle libosti.
\n
@@ -263,13 +263,13 @@
Vyberte řazení položek a skupin.
Zapojit se
Zapojte se a pomozte zvýšit stabilitu, zabezpečení a doplnění dalších funkcí.
- Na rozdíl od mnoha aplikací pro správu hesel je tato bez reklam, je svobodný software pod copyleft licencí a nesbírá žádné osobní údaje na svých serverech bez ohledu na to, jakou verzi používáte.
- Zakoupením varianty „pro“ získáte přístup k tomuto vizuálnímu stylu a hlavně pomůžete uskutečnění komunitních projektů.
+ Na rozdíl od mnoha aplikací pro správu hesel je tato <strong>bez reklam</strong>, je <strong>svobodný software pod copyleft licencí</strong> a nesbírá žádné osobní údaje na svých serverech bez ohledu na to, jakou verzi používáte.
+ Zakoupením varianty \"pro\" získáte přístup k tomuto <strong>vizuálnímu stylu</strong> a hlavně pomůžete <strong>uskutečnění komunitních projektů.</strong>
Tento <strong>vizuální styl</strong> je k dispozici díky vaší štědrosti.
Pro zajištění svobody nás všech a pokračování aktivity počítáme s Vaším <strong>přispěním.</strong>
Tato funkce je <strong>ve vývoji</strong> a potřebuje Váš <strong>příspěvek</strong>, aby byla brzy k dispozici.
Zakoupením <strong>pro</strong> varianty,
- Podpořením vývoje,
+ <strong>Podpořením vývoje</strong>,
povzbudíte vývojáře k doplnění <strong>nových funkcí</strong> a <strong>opravám chyb</strong> dle vašich připomínek.
Mnohokrát děkujeme za Váš příspěvek.
Tvrdě pracujeme na brzkém vydání této funkce.
@@ -300,7 +300,7 @@
Vibrovat při stisku klávesy
Vydat zvuk při stisku klávesy
Režim výběru
- Neshoďte aplikaci…
+ Nezavírejte aplikaci…
K uzamknutí stiskněte Zpět
Zamknout obrazovku, pokud uživatel stiskne tlačítko Zpět v hlavním panelu
Vymazat při ukončení
@@ -358,13 +358,13 @@
Interval musít být mezi %1$d a %2$d vteřinami.
Token musí obsahovat mezi %1$d a %2$d číslicemi.
%1$s s totožným UUID %2$s již existuje.
- Zakládání databáze…
+ Zakládám databázi…
Nastavení zabezpečení
Nastavení hlavního klíče
Databáze obsahuje duplikátní UUID.
Opravit chybu založením nového UUID pro duplikáty a pokračovat\?
Databáze otevřena
- Kopírovat kolonky záznamů pomocí schránky svého zařízení
+ Kopírovat kolonky záznamů pomocí schránky Vašeho zařízení
K snadnějšímu otevření databáze použijte rozšířené odemknutí
Komprese dat
Komprese dat snižuje velikost databáze
@@ -387,7 +387,7 @@
Databázi nebylo možné uložit.
Uložit databázi
Vysypat koš
- Provádění příkazu…
+ Provádím příkaz…
Natrvalo smazat vybrané uzly\?
Úložiště klíčů není řádně inicializováno.
Název skupiny
@@ -399,9 +399,9 @@
Akce auto-klávesy
Akce klávesy \"Jít\" po stisknutí klávesy \"Kolonka\"
Stáhnout %1$s
- Zahajování…
+ Zahajuji…
Probíhá: %1$d%%
- Dokončování…
+ Dokončuji…
Dokončeno!
Skrýt záznamy kterým skončila platnost
Nejsou zobrazovány záznamy kterým skončila platnost
@@ -419,9 +419,9 @@
Skrýt chybné odkazy na databáze
Skrýt chybné odkazy v seznamu nedávných databází
Udělit právo zápisu pro uložení změn v databázi
- KeePassDX © %1$d Kunzisoft je open source a bez reklam.
-\nJe poskytován jak je, od licencí GPLv3, bez jakékoli záruky.
- Abychom si udrželi svoji svobodu, mohli opravovat chyby, přidávat nové funkce a byli pořád aktivní, počítáme s Vaším přispěním.
+ KeePassDX © %1$d Kunzisoft je <strong>open source</strong> a <strong>bez reklam</strong>.
+\nJe poskytován jak je, pod licencí <strong>GPLv3</strong>, bez jakékoli záruky.
+ Abychom si <strong>udrželi svoji svobodu</strong>, <strong>mohli opravovat chyby</strong>, <strong>přidávat nové funkce</strong> a <strong>byli pořád aktivní</strong>, počítáme s Vaším <strong>přispěním</strong>.
Nepodařilo se vytvořit soubor databáze.
Přidat přílohu
Zahodit
@@ -469,9 +469,9 @@
Ukáže UUID propojené se záznamem nebo skupinou
Ukázat UUID
Uložení dat není povoleno, je-li databáze v režimu pouze pro čtení.
- Po dokončení vyplňování formuláře se zeptat na uložení dat
- Zeptat se před uložením
- Pokusit se uložit údaje hledání pro příští použití, vybíráte-li manuálně záznam
+ Po dokončení vyplnění formuláře se tázat na uložení dat
+ Tázat se před uložením
+ Pokusit se uložit údaje hledání pro příští použití, vybíráte-li záznam manuálně
Uložit výsledky vyhledávání
Zavřít databázi po samovyplnění polí
Zavřít databázi
@@ -487,7 +487,7 @@
Uložit
Vyhledávání
Jméno kolonky již existuje.
- Uložení nové položky v režimu databáze pouze pro čtení není povoleno
+ Uložení nové položky v režimu databáze pouze pro čtení není dovoleno
Enter
Backspace
Vybrat záznam
@@ -516,7 +516,7 @@
Přechodné rozšířené odemknutí
Pro odstranění klíčů rozšířeného odemknutí klepnout
Abyste rychle odemknuli databázi, propojte své heslo s naskenovanou biometrikou nebo údaji zámku zařízení.
- Vypršení pokročilého odemknutí
+ Vypršení rozšířeného odemknutí
Obsah
Seznam nelze řádně sestavit.
URI databáze nelze načíst.
@@ -526,7 +526,7 @@
Přístup k souboru odebrán správcem souborů, uzavřete databázi a nově ji otevřete z jejího adresáře.
Znovu načíst data
Sloučit data, přepsat externí změny uložením databáze nebo databázi znovu načíst včetně nejnovějších změn.
- Informace obsažená ve Vašem databázovém souboru by změněna mimo aplikaci.
+ Informace obsažená ve Vašem databázovém souboru byla změněna mimo aplikaci.
GiB
MiB
KiB
@@ -592,15 +592,15 @@
Ukázat OTP token
Externí ikona
Vyberte položku…
- Zobrazit možnosti umožňující uživateli si vybrat položku z databáze
+ Zobrazit možnosti umožňující uživateli vybrat si položku z databáze
Ruční výběr
Jméno symbolu
Nepovolili jste aplikaci použít přesný alarm. Výsledkem je, že funkce požadující časovač nebudou provedeny v přesný okamžik.
Povolení
Štítky
Sloučit databázi
- Opětovné načtení databáze smaže všechny lokálně změněné data.
- Hash souboru není garantován protože Android umožňuje změnu souboru v běhu. Změňte koncovku souboru na .bin pro opravu integrity.
+ Opětovné načtení databáze smaže všechna lokálně změněná data.
+ Hash souboru není garantován, protože Android může soubor změnit v běhu. Změňte koncovku souboru na .bin pro správnou integritu.
Ponechat obrazovku zapnutou
Barva databáze
Wi-Fi
@@ -614,10 +614,10 @@
Zadejte barvu pozadí
Zadejte barvu popředí
Platnost skončila
- Zobrazí barvy popředí a pozadí v záznamu
+ Zobrazí barvy popředí a pozadí záznamu
Barvy záznamu
Běžný výraz
- Při zobrazování položky zabránit zhasnutí obrazovky
+ Při prohlížení záznamu držet obrazovku zapnutou
Otevření navigačního panelu
Zavření navigačního panelu
Převzít
@@ -645,4 +645,28 @@
VELKÁ PÍSMENA
Titulové Psaní
Počet znaků: %1$d
+ Umístění databáze je neznámé, akci na databázi nelze provést.
+ Automatické zadání
+ <strong>Nenačítají se žádná uživatelská data</strong>, tato aplikace se nespojuje s žádnými servery, pracuje pouze lokálně a plně respektuje soukromí uživatelů.
+ Hardwarový klíč
+ XML chybný.
+ Výzva byla již vyžádána
+ Odpověď byla již poskytnuta.
+ Odezvu nelze z výzvy obdržet.
+ Zrušeno uživatelem.
+ Ovladač pro %1$s je nutný.
+ Spojení z databáze V1 nelze provést.
+ Hardwarový klíč není podporován.
+ Klíč nemůže být prázdný.
+ Režim screenshotu
+ Dovolit aplikacím třetích stran zaznamenat nebo provést screenshot aplikace
+ Režim screenshotu
+ Navigační hlavička
+ Zaškrtávací kolonka hardwarového klíče
+ Čekám na požadavek na výzvu…
+ Čekám na odezvu výzvy…
+ Vybrat hardwarový klíč.
+ Poškozený soubor.
+ Pamatovat si hardwarové klíče
+ Udržuje znalost o použitých hardwarových klíčích
\ No newline at end of file
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 57a83afa5..a5ddc83bd 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -19,25 +19,25 @@
Danish translation by Frederik Svarre (fsvarre@gmail.com)
-->
Hjemmeside
- Android implementering af KeePass password manager
+ Android-implementering af KeePass-adgangskodeadministrator
Accepter
Tilføj post
Tilføj gruppe
Krypteringsalgoritme
- Tid udløbet
- Inaktiv tid, før databasen låses
- Program
- Indstillinger
+ Tidsudløb
+ Inaktiv tid før databasen låses
+ App
+ App-indstillinger
Parenteser
En filhåndtering, der accepterer intensionshandlingen ACTION_CREATE_DOCUMENT og ACTION_OPEN_DOCUMENT, er nødvendig for at oprette, åbne og gemme databasefiler.
Udklipsholder ryddet
- Udklipsfejl
- Nogle enheder, vil ikke lade programmer bruge udklipsholderen.
+ Udklipsholderfejl
+ Nogle enheder vil ikke lade apps bruge udklipsholderen.
Kunne ikke rydde udklipsholderen
- Udklipsholder timeout
- Varighed af opbevaring i udklipsholder (hvis det understøttes)
+ Udklipsholder tidsudløb
+ Varighed af opbevaring i udklipsholderen (hvis det understøttes)
Vælg for at kopiere %1$s til udklipsholder
- Opretter databasenøgle…
+ Henter databasenøgle…
Database
Dekrypterer databaseindhold…
Brug som standarddatabase
@@ -57,16 +57,16 @@
URL
Brugernavn
Arcfour stream cipher er ikke understøttet.
- Kunne ikke håndtere URI i KeePassDX.
+ Kunne ikke håndtere denne URI i KeePassDX.
Kunne ikke oprette fil
Kunne ikke læse databasen.
Sørg for, at stien er korrekt.
Indtast et navn.
Vælg en nøglefil.
Ikke nok hukommelse til at indlæse hele databasen.
- Der skal vælges mindst én kode for kodeordsgenerering.
+ Der skal vælges mindst én type af adgangskodegenerering.
Adgangskoderne er ikke ens.
- \"Transformation Runder\" er for stor. Sættes til 2147483648.
+ \"Transformationsrunder\" er sat for højt. Sættes til 2147483648.
Hver streng skal have et feltnavn.
Angiv et positivt heltal i feltet \"Længde\".
Feltnavn
@@ -86,15 +86,15 @@
Nøglefilen er tom.
Længde
Størrelse på listeelementer
- Tekststørrelse i elementliste
+ Tekststørrelse i elementlisten
Indlæser database…
Små bogstaver
Skjul adgangskoder
- Masker adgangskoder (***) som standard
+ Skjul adgangskoder (***) som standard
Om
Skift hovednøgle
Indstillinger
- Database indstillinger
+ Databaseindstillinger
Slet
Donér
Rediger
@@ -107,27 +107,27 @@
Bindestreg
Aldrig
Ingen søgeresultater
- Installer en web-browser til at åbne URL.
+ Installer en web-browser for at åbne denne URL.
Opretter ny database…
Arbejder…
Beskyttelse
- KeePassDX behøver skrivetilladelse for at ændre i databasen.
+ Afhængigt af din filhåndtering er det muligt, at KeePassDX ikke har tilladelse til at skrive i dit lager.
Fjern
Rod
Transformationsrunder
- Yderligere krypteringsrunder giver højere beskyttelse mod brute-force angreb, men kan virkelig forsinke læsnings- og skrivehastigheden.
+ Yderligere krypteringsrunder giver højere beskyttelse mod brute-force angreb, men kan forsinke læsnings- og skrivehastigheden markant.
Gemmer database…
Mellemrum
Naturlig rækkefølge
Speciel
Søg
Understregning
- Database-versionen er ikke understøttet.
+ Database-version ikke understøttet.
Store bogstaver
Version %1$s
Angiv en adgangskode og/eller en nøglefil til at låse databasen op.
\n
-\nHusk at gemme en kopi af .kdbx filen i et sikkert sted efter hver ændring.
+\nHusk at gemme en kopi af din databasefil et sikkert sted efter hver ændring.
- Lille
- Mellem
@@ -140,10 +140,10 @@
Tillad
Databasen kunne ikke indlæses.
Kunne ikke indlæse nøglen. Prøv at reducere KDF \"hukommelsesforbrug\".
- Kunne ikke aktivere autofyld tjenesten.
+ Kunne ikke aktivere autofyld-tjenesten.
Kunne ikke finde filen. Prøv at åbne den fra filhåndtering.
Vis brugernavne
- Vis brugernavne i postlister
+ Viser brugernavne i postlister
Kopi af %1$s
Formularudfyldning
Kopier
@@ -151,12 +151,12 @@
Indsæt
Annuller
Skrivebeskyttet
- Modificerbar
+ Kan ændres
Skrivebeskyttet
- Databasekrypteringsalgoritme anvendt for alle data.
+ Databasekrypteringsalgoritme anvendt for alle data
For at generere nøglen til krypteringsalgoritmen, omdannes hovednøglen ved hjælp af en tilfældigt saltet nøgleafledningsfunktion.
Hukommelsesforbrug
- Hukommelse, som anvendes af nøgleafledningsfunktion.
+ Hukommelse, som anvendes af nøgleafledningsfunktionen.
Parallelitet
Grad af parallelitet (dvs. antallet af tråde), som anvendes af nøgleafledningsfunktion.
Sorter
@@ -170,34 +170,34 @@
Adgang
Advarsel
Undgå adgangskodetegn uden for tekstkodningsformatet i databasefilen (ukendte tegn konverteres til samme bogstav).
- Bekræft brug af ingen adgangskode til beskyttelse mod oplåsning\?
+ Fortsæt uden adgangskode til beskyttelse mod oplåsning\?
Fortsæt uden krypteringsnøgle\?
Krypteret adgangskode er gemt
- Databasen har endnu ikke en adgangskode.
+ Denne database har endnu ikke gemt legitimationsoplysninger.
Historik
Udseende
Generelt
Autoudfyld
KeePassDX formularudfyldning
Log ind med KeePassDX
- Indstil standard autoudfyldservice
- Aktiver autofyldning for hurtigt at udfylde formularer i andre programmer
- Genereret kodeordslængde
+ Indstil standard autoudfyldningstjeneste
+ Aktiver autofyldning for hurtigt at udfylde formularer i andre apps
+ Genereret adgangskodelængde
Angiver standardlængden for genererede adgangskoder
Adgangskodetegn
Angiv tilladte tegn for adgangskodegenerator
Udklipsholder
- Udklipsholdermeddelelser
- Vis udklipsholder for at kopiere felter når en post vises
- Hvis automatisk sletning af udklipsholder mislykkes, slet historikken manuelt.
+ Udklipsholder-notifikationer
+ Vis udklipsholder-notifikationer for at kopiere felter, når en post vises
+ Hvis den automatiske sletning af udklipsholderen ikke lykkes, skal du slette dens historik manuelt.
Lås
Skærmlås
- Lås databasen, når skærmen er slukket
- Fingeraftryk
+ Lås databasen få sekunder efter at skærmen slukkes
+ Avanceret oplåsning
Biometrisk oplåsning
- Giver mulighed for at scanne biometriske for at åbne databasen
+ Giver mulighed for at scanne dine biometriske data for at åbne databasen
Slet krypteringsnøgler
- Slet alle krypteringsnøgler, der er relateret til biometrisk genkendelse
+ Slet alle krypteringsnøgler, der er relateret til avanceret oplåsningsgenkendelse
Funktionen kunne ikke startes.
Enheden kører Android %1$s, men har brug for %2$s eller nyere.
Kunne ikke finde den tilsvarende hardware.
@@ -206,34 +206,34 @@
Tildel en hovednøgle
Opret en ny database
Brug af papirkurven
- Flyt grupper og poster til gruppen \"Papirkurven\" før den slettes
+ Flyt grupper og poster til \"Papirkurv\" før sletning
Feltskrifttype
Skift skrifttypen, der anvendes i felter, for at forbedre tegnsynlighed
Udklipsholder tillid
- Tillad kopiering af adgangskoden og beskyttede felter kopieres til udklipsholderen
- Advarsel: Udklipsholder deles af alle programmer. Hvis følsomme data er kopieret, kan andet software gendanne den.
+ Tillad kopiering af postens adgangskode og beskyttede felter til udklipsholderen
+ Advarsel: Udklipsholderen deles af alle apps. Hvis følsomme data er kopieret, kan andet software gendanne den.
Databasenavn
- Database beskrivelse
+ Database-beskrivelse
Databaseversion
Tekst
Brugerflade
Øvrige
Tastatur
- Magi keyboard
+ Magikeyboard
Aktiver et brugerdefineret tastatur, der udfylder adgangskoder og alle identitetsfelter
Tillad ingen hovednøgle
Tillader at trykke på knappen \"Åbn\", hvis der ikke er valgt nogen legitimationsoplysninger
Skrivebeskyttet
- Åbn database skrivebeskyttet som standard
- Pædagogiske tips
- Fremhæv elementer for at lære, hvordan programmet fungerer
- Nulstil pædagogiske tip
- Vis al uddannelsesinformation igen
- Nulstilling af pædagogiske tips
- Opret databasefilen
- Opret den første adgangskodeadministrationsfil.
+ Åbn som standard databasen skrivebeskyttet
+ Praktiske tips
+ Fremhæv elementer for at lære, hvordan appen fungerer
+ Nulstil praktiske tips
+ Vis al vejledning igen
+ Nulstilling af praktiske tips
+ Opret din databasefil
+ Opret din første adgangskodeadministrationsfil.
Åbn en eksisterende database
- Åbn den tidligere database fil fra filhåndtering for at fortsætte med at bruge den.
+ Åbn den tidligere databasefil fra din filhåndtering for at fortsætte med at bruge den.
Tilføj elementer til databasen
Poster hjælper med at administrere digitale identiteter.
\n
@@ -241,56 +241,56 @@
Søg i poster
Indtast titel, brugernavn eller indhold af andre felter for at hente adgangskoder.
Rediger posten
- Rediger post med brugerdefinerede felter. Pool data kan refereres mellem forskellige indtastningsfelter.
+ Rediger post med brugerdefinerede felter. Pool-data kan refereres mellem forskellige indtastningsfelter.
Opret en stærk adgangskode
- Generer en stærk kodeord til at forbinde elementet, definer det i henhold til kriteriet for formularen og glem ikke et sikkert kodeord.
+ Generer en stærk adgangskode til din post, og definer den nemt i henhold til kriterierne i formularen, og glem ikke en sikker adgangskode.
Tilføj brugerdefinerede felter
Registrer et ekstra felt, tilføj en værdi og beskyt det eventuelt.
Lås databasen op
Skrivebeskyt databasen
Skift åbningstilstanden for sessionen.
\n
-\nI \"skrivebeskyttet\" tilstand forhindres utilsigtede ændringer i databasen.
-\nI \"skrivetilstand\" kan man tilføje, slette eller redigere alle elementerne, som det ønskes.
+\nI \"Skrivebeskyttet\" tilstand forhindres utilsigtede ændringer i databasen.
+\nI \"Kan ændres\" kan du tilføje, slette eller redigere alle elementerne, som det ønskes.
Kopier et felt
- Kopier nemt et felt og indsæt det hvor det ønskes
+ Kopierede felter kan indsættes hvor som helst.
\n
-\nBrug flere formular udfyldninsmetoder. Brug den der foretrækkes.
+\nBrug den metode til udfyldelse af formularer, du foretrækker.
Lås databasen
Lås hurtigt databasen, indstil til at låse efter et stykke tid, og når skærmen slukkes.
- Sorter elementer
+ Sortering af elementer
Vælg hvordan poster og grupper er sorteret.
Deltag
- Bidrag til at øge stabiliteten, sikkerheden og med at tilføje flere funktioner.
- I modsætning til andre programmer til adgangskodeadministration er denne <strong>annoncefri</strong>, <strong>copyleft fri software</strong>, og indsamler ikke personlige data, uanset hvilken version der bruges.
- Ved at købe pro-versionen, er der adgang til <strong>visuel stil</strong>, og det vil især hjælpe <strong>gennemførelsen af lokale projekter.</strong>
- Denne <strong>visuelle stil</strong> er tilgængelige takket være bidrag.
- For at bevare uafhængighed og altid at være aktiv, håber vi på dit bidrag.
+ Hjælp med at øge stabiliteten og sikkerheden og med at tilføje flere funktioner.
+ I modsætning til andre apps til adgangskodeadministration er denne <strong>reklamefri</strong>, <strong>copyleft fri software</strong>, og indsamler ikke personlige data, uanset hvilken version der bruges.
+ Ved at købe pro-versionen får du adgang til <strong>visuel stil</strong>, og du vil især hjælpe med <strong>gennemførelsen af fællesskabets projekter.</strong>
+ Denne <strong>visuelle stil</strong> er tilgængelig takket være din generøsitet.
+ For at vi kan bevare vores frihed og altid være aktive, er vi afhængige af dit <strong>bidrag.</strong>
Funktionen er <strong>under udvikling</strong>, og det kræver <strong>bidrag</strong>, for snart at være tilgængelig.
Ved at købe <strong>pro</strong> versionen,
Ved at <strong>bidrage</strong>,
- tilskyndes udviklerne til at lave <strong>nye funktioner</strong> og <strong>rette fejl</strong> i henhold bemærkninger.
- Tak for bidrag.
+ tilskyndes udviklerne til at lave <strong>nye funktioner</strong> og <strong>rette fejl</strong> i henhold dine bemærkninger.
+ Mange tak for dit bidrag.
Vi arbejder hårdt på hurtigt at frigive denne funktion.
Husk at holde din app opdateret ved at installere den nyeste version.
Hent
Bidrag
- Tema
- Tema, der bruges i programmet
+ App-tema
+ Tema, der bruges i appen
Ikonpakke
- Ikonpakke, der anvendes
- Magi keyboard
- Magi keyboard (KeePassDX)
+ Ikonpakke, der anvendes i appen
+ Magikeyboard
+ Magikeyboard (KeePassDX)
Magikeyboard indstillinger
Post
Udløbstid
- Meddelelsesinfo
- Vis en meddelelse, når en post er til rådighed
+ Notifikationsinfo
+ Vis en notifikation, når en post er til rådighed
Post
%1$s til rådighed på Magikeyboard
%1$s
Ryd ved lukning
- Luk databasen, når meddelse lukkes
+ Luk databasen, når notifikation lukkes
Udseende
Tastaturtema
Taster
@@ -300,49 +300,49 @@
Tidsudløb for at rydde indtastning
Noter
Valgstilstand
- Luk ikke programmet…
+ Luk ikke appen…
Tryk på \'Tilbage\' for at låse
Lås databasen, når der klikkes på tilbage-knappen fra startskærmen
Ryd ved lukning
- Lås databasen, når udklipsholderens varighed udløber, eller meddelelsen lukkes, når du begynder at bruge den
+ Lås databasen, når udklipsholderens varighed udløber, eller notifikationen lukkes, efter at du begynder at bruge den
Papirkurven
Valg af indtastning
- Vis indtastningsfelter i Magikeyboard, når der vises en post
+ Når du ser en post i KeePassDX, skal Magikeyboard udfyldes med den pågældende post
Slet adgangskode
- Sletter adgangskoden der er angivet efter et forsøg på at oprette forbindelse til en database
+ Sletter den adgangskode, der er indtastet efter et forsøg på at oprette forbindelse til en database
Åbn fil
- Undernode
- Tilføj knude
+ Undernoder
+ Tilføj node
Tilføj post
Tilføj gruppe
Filoplysninger
Afkrydsningsfelt for adgangskode
Afkrydsningsfelt for nøglefil
Gentag for at skifte synlighed for adgangskode
- Indtastningsikon
+ Ikon for post
Adgangskodegenerator
Længde på adgangskode
Tilføj felt
Fjern felt
UUID
Vis antal poster
- Vise antallet af poster i en gruppe
- Post kan ikke flyttes her til.
- Post kan ikke kopieres her til.
+ Viser antallet af poster i en gruppe
+ Du kan ikke flytte en post hertil.
+ Post kan ikke kopieres hertil.
Baggrund
Opdater
Luk felter
Kan ikke oprette database med denne adgangskode og nøglefil.
Avanceret oplåsning
Biometrisk
- Åbn automatisk biometrisk prompt
- Spørg automatisk efter biometrisk oplåsning, hvis databasen er konfigureret til at bruge det
- Aktiver
+ Prompt til automatisk åbning
+ Spørg automatisk efter avanceret oplåsning, hvis databasen er konfigureret til at bruge det
+ Aktivér
Deaktiver
Hovednøgle
Sikkerhed
Historik
- Indstilling af engangsadgangskode (OTP)
+ Indstil engangsadgangskode (OTP)
OTP-type
Hemmelig
Periode (sekunder)
@@ -351,26 +351,26 @@
Algoritme
OTP
Ugyldig OTP-hemmelighed.
- Der skal angives mindst en legitimationsoplysning.
+ Der skal angives mindst én legitimationsoplysning.
En gruppe kan ikke kopieres her.
Den hemmelige nøgle skal være i Base32-format.
Tæller skal være mellem %1$d og %2$d.
- Perioden skal være mellem %1$d og %2$d sekunder.
+ Perioden skal være på mellem %1$d og %2$d sekunder.
Token skal indeholder %1$d til %2$d cifre.
%1$s med det samme UUID %2$s findes allerede.
Opretter database…
Sikkerhedsindstillinger
Indstillinger for hovednøgle
- Databasen indeholder dublerede UUID\'er.
+ Databasen indeholder UUID-dubletter.
Løs problemet ved at generere nye UUID\'er for dubletter og fortsætte\?
Database åbnet
Kopier indtastningsfelter ved hjælp af enhedens udklipsholder
Brug avanceret oplåsning for at gøre det lettere at åbne en database
Datakomprimering
Datakomprimering reducerer databasens størrelse
- Max. antal
- Begræns antallet af historikposter pr. indtastning
- Max. størrelse
+ Maksimalt antal
+ Begræns antallet af historikelementer pr. post
+ Maksimal størrelse
Begræns historikstørrelsen pr. post
Anbefalet fornyelse
Anbefal ændring af hovednøglen (dage)
@@ -385,14 +385,14 @@
Gzip
Indstillinger for enhedens tastatur
Databasen kunne ikke gemmes.
- Gem database
+ Gem data
Tøm papirkurven
Udfører kommandoen…
Slet markerede noder permanent\?
Nøglelageret er ikke korrekt initialiseret.
Papirkurvsgruppe
Gem automatisk database
- Gem databasen efter hver en vigtig handling (i tilstanden \"Modificerbar\")
+ Gem databasen efter hver en vigtig handling (i tilstanden \"Kan ændres\")
Vedhæftninger
Gendan historik
Slet historik
@@ -401,96 +401,96 @@
Hent %1$s
Initialiserer…
I gang: %1$d%%
- Færdiggørelse…
- Komplet!
+ Færdiggører…
+ Færdig!
Skjul udløbne poster
Udløbne poster vises ikke
Kontakt
Bidrag
Tilbagemelding
KeePassDX ©%1$d Kunzisoft er <strong>open source</strong> og <strong>uden reklamer</strong>.
-\nDet leveres som det er under <strong>GPLv3</strong> licens uden nogen garanti.
- For at <strong>holde vores frihed</strong>, <strong>rette fejl</strong>, <strong>tilføje funktioner</strong> og <strong>at være altid aktiv</strong>, regner vi med <strong>bidrag</strong>.
+\nDen leveres som den er, under <strong>GPLv3</strong> licens, uden nogen form for garanti.
+ For at <strong>vi kan bevare vores frihed</strong>, <strong>rette fejl</strong>, <strong>tilføje funktioner</strong> og <strong>altid være aktive</strong>, er vi afhængige af <strong>bidrag</strong>.
Hurtig søgning
- Anmod om en søgning når en database åbnes
+ Anmod om søgning ved åbning af en database
Husk placeringer af databaser
Holder styr på, hvor databaserne gemmes
Husk placering af nøglefiler
Holder styr på, hvor nøglefiler gemmes
Vis seneste filer
Vis placeringer af de seneste databaser
- Skjule brudte databaselinks
+ Skjul brudte databaselinks
Skjul brudte links på listen over seneste databaser
Giv fil skriveadgang for at gemme databasændringer
- Sæt op engangs-adgangskode-styring (HOTP / TOTP) for at generere et token anmodet af tofaktor-autentisering (2FA).
+ Indstil engangs-adgangskode-styring (HOTP / TOTP) for at generere et token anmodet om til tofaktor-autentisering (2FA).
Opsætning af OTP
Databasefilen kunne ikke oprettes.
- Tilføj vedhæng
+ Tilføj vedhæftning
Kassér
- Kasser ændringer\?
+ Kassér ændringer\?
Valider
Viser låseknappen i brugergrænsefladen
Vis låseknap
Indstillinger for automatisk udfyldning
- Adgang til filen tilbagekaldt af filadministratoren
+ Adgang til filen tilbagekaldt af filhåndteringen
Etiketten findes allerede.
- Genstart programmet, der indeholder formularen for at aktivere blokeringen.
+ Genstart appen, der indeholder formularen for at aktivere blokeringen.
Bloker autofyld
Blokeringsliste, der forhindrer automatisk udfyldning af webdomæner
- Blokeringsliste for webdomæne
- Blokeringsliste der forhindrer automatisk udfyldning af programmer
- Blokeringsliste for program
+ Blokeringsliste for webdomæner
+ Blokeringsliste der forhindrer automatisk udfyldning af apps
+ Blokeringsliste for applikation
Skift automatisk tilbage til det forrige tastatur efter udførelse af \"Automatisk tastehandling\"
Automatisk tastehandling
Skift automatisk tilbage til det forrige tastatur på databasens legitimationsskærm
- Skærmbilledet til databaselegitimationsoplysninger
- Skifte tastatur
+ Skærm til databaselegitimationsoplysninger
+ Skift tastatur
Filter
Søg på webdomæner med begrænsninger på underdomæner
- Underdomæne søgning
+ Underdomæne-søgning
Teksten stemmer ikke overens med det ønskede element.
Tilføj element
- Viser UUID\'en, der er knyttet til en post
+ Viser UUID\'en, der er knyttet til en post eller en gruppe
Vis UUID
Overfør %1$s
Vedhæft fil
- Lagring af data er ikke tilladt for en database, der er åbnet som skrivebeskyttet.
- Bed om at gemme data, når en formular er valideret
- Bed om at gemme data
+ Det er ikke tilladt at gemme data i en database, der er åbnet som skrivebeskyttet.
+ Spørg om du vil gemme data, når en formular er udfyldt
+ Spørg om du vil gemme data
Gem søgeoplysninger
Luk databasen efter en markering af autofyld
Luk database
Skift automatisk tilbage til det forrige tastatur efter låsning af databasen
Lås databasen
Gem delte oplysninger
- Anmeldelse
- Fjerner vedhæftede filer indeholdt i databasen, men ikke knyttet til en post
+ Notifikation
+ Fjerner vedhæftede filer, der findes i databasen, men som ikke er knyttet til en post
Fjern ikke-sammenkædede data
Data
Biometrisk sikkerhedsopdatering påkrævet.
- Indholdet af nøglefilen bør aldrig ændres og bør i bedste fald indeholde tilfældigt genererede data.
+ Indholdet af nøglefilen bør aldrig ændres, og optimalt set bør den indeholde tilfældigt genererede data.
Det anbefales ikke at tilføje en tom nøglefil.
Fjern alligevel disse data\?
Fjernelse af ikke-linkede data kan mindske størrelsen på databasen, men kan også slette data, der bruges til KeePass-plugins.
Tilføj alligevel filen\?
- Overførelsen af filen erstatter den eksisterende.
- En KeePass database formodes kun at indeholde små hjælpefiler (såsom PGP nøglefiler).
+ Overførsel af denne fil vil erstatte den eksisterende fil.
+ En KeePass-database bør kun indeholde små hjælpefiler (såsom PGP-nøglefiler).
\n
\nDatabasen kan blive meget stor og reducere ydeevnen med denne overførelse.
Slet alle noder permanent fra papirkurven\?
Registreringstilstand
- Gem tilstand
+ Gem-tilstand
Søgetilstand
Det er ikke tilladt at gemme et nyt element i en skrivebeskyttet database
- Oplysninger om legitimationsoplysninger
+ Legitimationsoplysninger
Der er ikke tilmeldt biometriske legitimationsoplysninger eller enhedsoplysninger.
Overfør en vedhæftet fil til posten for at gemme vigtige eksterne data.
- Forsøger at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg
- Forsøger at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg
+ Forsøger at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg, så det bliver lettere at bruge dem senere
+ Forsøg at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg, så det bliver lettere at bruge dem senere
Feltnavnet findes allerede.
Brugerdefineret
Standard
- Vælg lys eller mørk tema
+ Vælg lyst eller mørkt tema
Temalysstyrke
GiB
MiB
@@ -503,68 +503,170 @@
Tilbage til forrige tastatur
Brugerdefinerede felter
Slet alle krypteringsnøgler relateret til avanceret oplåsningsgenkendelse\?
- Tryk for at slette avancerede oplåsningstaster
+ Tryk for at slette avancerede oplåsningsnøgler
Indhold
Indtast adgangskoden, og klik derefter på denne knap.
Kunne ikke initialisere avanceret oplåsningsprompt.
Fejl ved avanceret oplåsning: %1$s
- Kunne ikke genkende avanceret oplåsning
- Den avancerede oplåsningsnøgle kan ikke læses. Slet den, og gentag proceduren for genkendelse af oplåsning.
+ Kunne ikke genkende aftryk til avanceret oplåsning
+ Den avancerede oplåsningsnøgle kan ikke læses. Slet den og gentag proceduren for genkendelse af oplåsning.
Åbn database med avanceret oplåsningsgenkendelse
Avanceret oplåsningsgenkendelse
- Overskriv de eksterne ændringer ved at gemme databasen eller genindlæse den med de seneste ændringer.
+ Flet data, overskriv de eksterne ændringer ved at gemme databasen eller genindlæs den med de seneste ændringer.
Oplysningerne i databasefilen er blevet ændret uden for appen.
Slet avanceret oplåsningsnøgle
- Genindlæs database
+ Genindlæs data
Fildataene findes allerede.
Der opstod en fejl under overførsel af fildataene.
Filen, du prøver at overføre, er for stor.
Listen kan ikke genopbygges korrekt.
Database-URI kan ikke hentes.
Oplysninger om engangsadgangskode
- Uddrag databasens legitimationsoplysninger med biometriske data
- Advarsel: hovedadgangskoden skal stadig huskes, hvis der bruges biometrisk genkendelse.
- Åbn biometriske forespørgsel for at låse databasen op
+ Udtræk database legitimationsoplysninger med avancerede oplåsningdata
+ Du skal stadig huske din primære legitimationsoplysning, hvis du bruger avanceret oplåsningsgenkendelse.
+ Åbn den avancerede låseprompt for at låse databasen op
Adgang til filen tilbagekaldt af filhåndteringsprogrammet, luk databasen og genåbn den fra dens placering.
Der opstod en fejl under udførelsen af en handling på databasen.
- Der opstod en fejl med at fjerne fildata.
- Den existerende OTP type kunne ikke genkendes, den kan være tiden er udløbet for at lave dette token.
- Sammenkæd din adgangskode, med din scannede biometriske oplysninger eller enheds legitimationsoplysninger for hurtigt oplåsning af din database.
+ Der opstod en fejl under fjernelsen af fildata.
+ Den existerende OTP-type kunne ikke genkendes. Det kan være, at dens validering ikke længere genererer token korrekt.
+ Sammenkæd din adgangskode med din scannede biometriske data eller enhedens legitimationsoplysninger for hurtigt at låse din database op.
Enter
Varigheden af avanceret oplåsning, før indholdet slettes
- Giver dig mulighed for at bruge dine enhedsoplysninger for at åbne databasen
+ Giver dig mulighed for at bruge dine enhedsoplysninger til at åbne databasen
Oplåsning via enhedsoplysninger
- Forsøg på at vise forslag til automatisk udfyldning direkte fra et kompatibelt tastatur
+ Forsøg at vise forslag til automatisk udfyldning direkte fra et kompatibelt tastatur
Indbyggede forslag
Tilbagetast
- Avanceret oplåsningstimeout
- Avanceret oplåsning udløbsdato
+ Tidsudløb for avanceret oplåsning
+ Udløb af avanceret oplåsning
Gem ikke krypteret indhold for at bruge avanceret oplåsning
- Midlertidig biometrisk oplåsning
- Enhedsoplysninger
+ Midlertidig avanceret oplåsning
+ Enhedens legitimation
Egenskaber
- Åbn den biometrisk genkendelse for at gemme legitimationsoplysninger
+ Åbn den avancerede låseprompt for at gemme legitimationsoplysninger
Fejl under eksport af app-egenskaber
App-egenskaber eksporteret
Fejl under importering af app-egenskaber
App-egenskaber importeret
KeePassDX-egenskaber til at administrere app-indstillinger
- Opret en fil til at eksportere app-egenskaber
+ Opret en fil for at eksportere app-egenskaber
Eksporter app-egenskaber
Vælg en fil for at importere app-egenskaber
- Importer appegenskaber
- Du kan flytte en gruppe her.
- Dette ord er reseveret og kan ikke bruges.
+ Importer app-egenskaber
+ Du kan ikke flytte en gruppe hertil.
+ Dette ord er reserveret og kan ikke bruges.
Udløbet
Tags
- Database farve
+ Databasefarve
Skabeloner
Søgbar
- Arve
+ Nedarv
Brugerdefinerede data
- Søg filtre
+ Søgefiltre
Nuværende gruppe
Nummer
Navn
Type
+ Antal ord i adgangssætning
+ Der skelnes mellem store og små bogstaver
+ Almindeligt udtryk
+ Adgangssætning
+ Farvelæg adgangskoder
+ Farvelæg adgangskodetegn efter type
+ Adskillelsestegn
+ Ignorer tegn
+ små bogstaver
+ STORE BOGSTAVER
+ Overskriftstørrelse
+ Debit-/kreditkort
+ ID-kort
+ Kryptovaluta-wallet
+ CVV
+ Konto
+ Bank
+ Seed
+ Medlemskab
+ Flet data
+ Eksternt ikon
+ Genindlæsning af databasen vil slette de lokalt ændrede data.
+ Du har ikke givet appen tilladelse til at bruge en nøjagtig alarm. Som følge heraf vil de funktioner, der kræver en timer, ikke blive udført med et nøjagtigt tidspunkt.
+ Tilladelse
+ Skabelongruppe
+ Manuelt valg
+ Hold skærmen tændt, når du ser på posten
+ Hold skærmen tændt
+ Søgeskærm
+ Skift automatisk tilbage til det tidligere tastatur på søgeskærmen
+ Entropi: Høj
+ Entropi: Beregn…
+ Mindst et tegn fra hver
+ Forgrundsfarve for post
+ Baggrundsfarve for post
+ Holder
+ Vælg en hardwarenøgle.
+ XML forkert udformet.
+ Kan ikke flette fra en database V1.
+ Svar allerede givet.
+ Annulleret af bruger.
+ Driver til %1$s er påkrævet.
+ Databaseplacering er ukendt, databasehandling kan ikke udføres.
+ Hardwarenøgle er ikke understøttet.
+ Nøglen må ikke være tom.
+ Der er allerede anmodet om udfordring
+ Kan ikke få svaret fra udfordringen.
+ Vis OTP-token
+ Husk hardwarenøgler
+ Holder styr på de anvendte hardwarenøgler
+ En database er allerede åben, luk den først for at åbne den nye
+ Filens hash er ikke garanteret, fordi Android kan ændre dataene undervejs. Ændr filendelsen til .bin for at sikre korrekt integritet.
+ Vælg post…
+ Brug af skabeloner
+ Vis mulighed for at lade brugeren vælge databasepost
+ Skærmbillede-tilstand
+ Tillad tredjepartsapps at optage eller tage skærmbilleder af appen
+ Udelad tvetydige tegn
+ Antal tegn: %1$d
+ Skærmbillede-tilstand
+ Version
+ Udstedelsessted
+ E-mailadresse
+ Wi-fi
+ E-mail
+ SSID
+ Udstedelsesdato
+ Token
+ Afkrydsningsfelt for hardwarenøgle
+ PIN
+ Standard
+ IBAN
+ Skabelon
+ Beskadiget fil.
+ Ikonnavn
+ Viser OTP-tokens på listen over poster
+ Viser forgrund- og baggrundsfarver for en post
+ Brug dynamiske skabeloner til at udfylde felterne i en post
+ Denne funktion gemmer krypterede legitimationsdata i den sikre KeyStore på din enhed.
+\n
+\nAfhængigt af operativsystemets indbyggede API-implementering er den muligvis ikke fuldt funktionel.
+\nKontroller KeyStore\'s kompatibilitet og sikkerhed med producenten af din enhed og skaberen af den ROM, du bruger.
+ Entropi: %1$s bit
+ Overvej tegn
+ <strong>Der hentes ingen brugerdata</strong>, denne applikation opretter ikke forbindelse til nogen server, kører kun lokalt og respekterer fuldt ud brugernes privatliv.
+ Navigationsskuffe åben
+ Navigationsoverskrift
+ Navigationsskuffe lukke
+ Auto-indtastning
+ Auto-indtastning-sekvens
+ Gem en kopi til …
+ Flet fra …
+ Skabeloner
+ Venter på udfordringsanmodningen…
+ Venter på svar på udfordringen…
+ Hardware-nøgle
+ Offentlig nøgle
+ Privat nøgle
+ Banknavn
+ SWIFT/BIC
+ Sikker note
+ Postfarver
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 26de07c07..188d6ce87 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -150,7 +150,7 @@
Soll das Entsperren ohne Passwort wirklich möglich sein\?
Soll wirklich kein Verschlüsselungsschlüssel verwendet werden\?
- Aussehen
+ Erscheinungsbild
Generierte Passwortlänge
Legt die Standardlänge des generierten Passworts fest
Zwischenablage-Benachrichtigung
@@ -162,7 +162,7 @@
Pfad
Dateiname
Dieses Feature konnte nicht gestartet werden.
- Ermöglicht Ihre Biometrie zu scannen, um die Datenbank zu öffnen.
+ Ermöglicht das Scannen Ihrer biometrischen Daten, um die Datenbank zu öffnen
Moderne Entsperrung
Biometrische Entsperrung
Sperren
@@ -179,7 +179,7 @@
Dienst für automatisches Ausfüllen kann nicht aktiviert werden.
Kopie von %1$s
Formularausfüllung
- Verschlüsselungsalgorithmus der Datenbank wird für sämtliche Daten verwendet.
+ Verschlüsselungsalgorithmus der Datenbank wird für sämtliche Daten verwendet
Um den Schlüssel für den Verschlüsselungsalgorithmus zu generieren, wird der Hauptschlüssel umgewandelt, wobei ein zufälliger Salt in der Schlüsselberechnung verwendet wird.
Speichernutzung
Größe des Speichers, der für die Schlüsselableitung genutzt wird.
@@ -250,9 +250,9 @@
Auswählen, wie Einträge und Gruppen sortiert werden.
Mitmachen
Mithelfen, um Stabilität und Sicherheit zu verbessern sowie weitere Funktionen zu ermöglichen.
- Anders als viele andere Passwortmanager ist dieser <strong>werbefrei</strong>, <strong>quelloffen</strong> und unter einer <strong>Copyleft-Lizenz</strong>. Es werden keine persönlichen Daten gesammelt, in welcher Form auch immer, unabhängig von der verwendeten Version (kostenlos oder Pro).
+ Anders als viele andere Passwortmanager ist dieser <strong>werbefrei</strong>, <strong>quelloffen</strong> und unter einer <strong>Copyleft-Lizenz</strong>. Es werden keine persönlichen Daten gesammelt, egal in welcher Form, unabhängig von der verwendeten Version (kostenlos oder Pro).
Mit dem Kauf der Pro-Version erhalten Sie Zugriff auf diesen <strong>visuellen Stil</strong> und unterstützen insbesondere <strong>die Umsetzung gemeinschaftlicher Projekte.</strong>
- Dieser visuelle Stil ist dank Ihrer Großzügigkeit verfügbar.
+ Dieser <strong>visuelle Stil</strong> ist dank Ihrer Großzügigkeit verfügbar.
Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren <strong>Beitrag.</strong>
Diese Funktion ist <strong>in Entwicklung</strong> und erfordert <strong>Ihren Beitrag</strong>, um bald verfügbar zu sein.
Durch den Kauf der <strong>Pro-Version</strong>,
@@ -260,7 +260,7 @@
Sie ermutigen die Entwickler:innen, <strong>neue Funktionen</strong> einzuführen und gemäß Ihren Anmerkungen <strong>Fehler zu beheben</strong>.
Vielen Dank für Ihre Unterstützung.
Wir bemühen uns, diese Funktion bald zu veröffentlichen.
- Denken Sie daran, Ihre App auf dem neuesten Stand zu halten, indem Sie neue Versionen installieren.
+ Denken Sie daran, Ihre App durch die Installation neuer Versionen auf dem aktuellsten Stand zu halten.
Download
Unterstützen
Symbolpaket
@@ -285,7 +285,7 @@
\n„Schreibgeschützt“ verhindert unbeabsichtigte Änderungen an der Datenbank.
\nMit „Änderbar“ können Sie alle Elemente nach Belieben hinzufügen, löschen oder ändern.
Eintrag bearbeiten
- Datenbank kann nicht geladen werden.
+ Die Datenbank konnte nicht geladen werden.
Laden des Schlüssels fehlgeschlagen. Bitte versuchen, die „Speicherplatznutzung“ von KDF zu verringern.
Benutzernamen anzeigen
Benutzernamen in Eintragslisten anzeigen
@@ -303,16 +303,16 @@
%1$s
Beim Schließen löschen
Datenbank schließen, wenn die Benachrichtigung geschlossen wird
- Aussehen
+ Erscheinungsbild
Tastaturdesign
Tasten
Vibrierende Tastendrücke
Hörbare Tastendrücke
Auswahlmodus
Datenbank-Speicherorte merken
- Verfolgt, wo Datenbanken gespeichert sind
+ Verfolgt den Speicherort der Datenbanken
Schlüsseldatei-Speicherorte merken
- Verfolgt, wo Schlüsseldateien gespeichert sind
+ Verfolgt den Speicherort der Schlüsseldateien
Zuletzt verwendete Dateien anzeigen
Speicherort zuletzt verwendeter Datenbanken anzeigen
Defekte Datenbankverknüpfungen ausblenden
@@ -349,7 +349,7 @@
Hintergrund
Aktualisieren
Felder schließen
- Es ist nicht möglich, eine Datenbank mit diesem Passwort und dieser Schlüsseldatei zu erstellen.
+ Die Datenbank kann mit diesem Passwort und dieser Schlüsseldatei nicht erstellt werden.
Modernes Entsperren
Biometrisch
Aktivieren
@@ -385,7 +385,7 @@
Problem lösen, indem neue UUIDs für Duplikate generiert werden und danach fortfahren\?
Datenbank geöffnet
Eintragsfelder mithilfe der Zwischenablage des Geräts kopieren
- Modernes Entsperren verwenden, um eine Datenbank einfacher zu öffnen.
+ Modernes Entsperren verwenden, um eine Datenbank einfacher zu öffnen
Datenkompression
Datenkompression reduziert die Datenbankgröße
Maximale Anzahl
@@ -416,8 +416,8 @@
Anhänge
Historie wiederherstellen
Historie löschen
- Auto-Key-Aktion
- Aktion der Go-Taste, die automatisch nach dem Drücken einer Feldtaste ausgeführt wird
+ Automatische Tastenaktion
+ Nach dem Drücken einer Feldtaste automatisch die Eingabetaste ausführen
%1$s herunterladen
Initialisieren …
Fortschritt: %1$d%%
@@ -440,7 +440,7 @@
Datei Schreibrechte gewähren, um Datenbankänderungen zu speichern
Einrichten einer Einmal-Passwortverwaltung (HOTP / TOTP), um ein Token zu generieren, das für die Zwei-Faktor-Authentifizierung (2FA) angefordert wird.
OTP einrichten
- Es ist nicht möglich, eine Datenbankdatei zu erstellen.
+ Die Datenbankdatei kann nicht erstellt werden.
Anhang hinzufügen
Verwerfen
Änderungen verwerfen\?
@@ -454,7 +454,7 @@
Diese Bezeichnung existiert bereits.
Starten Sie die Anwendung, die das Formular enthält, neu, um die Sperrung zu aktivieren.
Automatisches Ausfüllen sperren
- Liste der Domains, auf denen ein automatisches Ausfüllen verhindert wird
+ Liste der Domains, für die ein automatisches Ausfüllen verhindert wird
Liste gesperrter Web-Domains
Liste der Apps, in denen ein automatisches Ausfüllen verhindert wird
Liste gesperrter Anwendungen
@@ -464,7 +464,7 @@
Element hinzufügen
Filter
Tastatur wechseln
- Auto-Key-Aktion
+ Automatische Tastenaktion
Datenbank-Anmeldebildschirm
Nach dem Ausführen der automatischen Tastenaktion automatisch zur vorherigen Tastatur wechseln
Auf dem Datenbank-Anmeldebildschirm automatisch zur vorherigen Tastatur wechseln
@@ -531,7 +531,7 @@
Geräteanmeldedaten entsperren
Geräteanmeldedaten
Geben Sie das Passwort ein und klicken Sie dann auf diesen Knopf.
- Dialog für modernes Entsperren konnte nicht gestartet werden.
+ Der Dialog für modernes Entsperren konnte nicht gestartet werden.
Fehler beim modernen Entsperren: %1$s
Abdruck zum modernen Entsperren nicht erkannt
Schlüssel zum modernen Entsperren nicht lesbar. Bitte löschen Sie ihn und wiederholen Sie die Prozedur zur Entsperrerkennung.
@@ -620,8 +620,8 @@
Bildschirm eingeschaltet lassen
Der Hash der Datei ist nicht garantiert, da Android die Daten im laufenden Betrieb ändern kann. Ändern Sie die Dateierweiterung in .bin, um die Integrität zu gewährleisten.
Den Bildschirm beim Ansehen des Eintrags eingeschaltet lassen
- Vorder- und Hintergrundfarbe in einem Eintrag anzeigen
- Auto-Type-Sequenz
+ Vorder- und Hintergrundfarbe für einen Eintrag anzeigen
+ Automatische Eingabefolge
Regulärer Ausdruck
Durchsuchbar
Vererbt
@@ -658,4 +658,27 @@
Anzahl der Zeichen: %1$d
Mehrdeutige Zeichen ausschließen
Groß-/Kleinschreibung des Titels
+ Hardwareschlüssel-Kontrollkästchen
+ Hardwareschlüssel
+ Hardwareschlüssel auswählen.
+ XML fehlerhaft.
+ Warte auf die Response-Antwort …
+ Warte auf die Challenge-Aufgabe …
+ Vom Benutzer abgebrochen.
+ Treiber für %1$s ist erforderlich.
+ Die Zusammenführung aus einer Datenbank V1 ist nicht möglich.
+ Der Speicherort der Datenbank ist unbekannt, Datenbank-Aktion kann nicht ausgeführt werden.
+ Der Hardwareschlüssel wird nicht unterstützt.
+ Der Schlüssel darf nicht leer sein.
+ Die Datei ist beschädigt.
+ Die Response-Antwort kann nicht abgerufen werden.
+ Die Challenge wurde bereits angefordert.
+ Die Response-Antwort wurde bereits übertragen.
+ Screenshot-Modus
+ Erlauben Sie Apps von Drittanbietern, Screenshots der Anwendung aufzuzeichnen oder zu erstellen
+ Screenshot-Modus
+ Hardwareschlüssel merken
+ Verfolgt die verwendeten Hardwareschlüssel
+ Automatische Eingabe
+ <strong>Es werden keine Benutzerdaten abgerufen</strong>, diese App stellt keine Verbindung zu einem Server her, arbeitet nur lokal und respektiert die Privatsphäre der Benutzer vollständig.
\ No newline at end of file
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 7c89c0961..0126f9460 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -138,14 +138,14 @@
Κρυπτογράφηση
Συνάρτηση παραγωγής κλειδιού
- Extended ASCII
+ Εκτεταμένο ASCII
Δέχομαι
Δεν ήταν δυνατή η ενεργοποίηση της υπηρεσίας αυτόματης συμπλήρωσης.
Δεν ήταν δυνατή η εύρεση αρχείου. Δοκιμάστε να το ανοίξετε ξανά από το πρόγραμμα περιήγησης αρχείων σας.
Αντιγραφή του %1$s
Γέμισμα Φόρμας
Προστασία εγγραφής
- Αλγόριθμος κρυπτογράφησης βάσης δεδομένων που χρησιμοποιείται για όλα τα δεδομένα.
+ Αλγόριθμος κρυπτογράφησης βάσης δεδομένων που χρησιμοποιείται για όλα τα δεδομένα
Για να δημιουργηθεί το κλειδί για τον αλγόριθμο κρυπτογράφησης, το κύριο κλειδί μετασχηματίζεται χρησιμοποιώντας μια τυχαία εμπλουτισμένη συνάρτηση παραγωγής κλειδιού.
Χρήση μνήμης
Ποσότητα μνήμης που θα χρησιμοποιηθεί από τη συνάρτηση παραγωγής κλειδιού.
@@ -267,7 +267,7 @@
\nΤο \"Προστατευμένο από εγγραφή\" αποτρέπει τυχόν μη επιθυμητές αλλαγές στη βάση δεδομένων.
\nΤο \"Τροποποιητικό\" σάς επιτρέπει να προσθέσετε, να διαγράψετε ή να τροποποιήσετε όλα τα στοιχεία όπως επιθυμείτε.
Επεξεργασία καταχώρησης
- Δεν ήταν δυνατή η φόρτωση της βάσης δεδομένων σας.
+ Δεν ήταν δυνατή η φόρτωση της βάσης δεδομένων.
Δεν ήταν δυνατή η φόρτωση του κλειδιού. Προσπαθήστε να μειώσετε την KDF \"Χρήση μνήμης\".
Εμφάνιση ονομάτων χρηστών
Εμφάνιση ονομάτων χρηστών σε λίστες καταχώρησης
@@ -607,7 +607,7 @@
Κρατήστε την οθόνη ενεργή όταν παρακολουθείτε την καταχώρηση
Διατηρήστε την οθόνη ενεργή
Χρώματα καταχώρησης
- Εμφανίζει τα χρώματα του προσκηνίου και του φόντου σε μια καταχώρηση
+ Εμφανίζει τα χρώματα του προσκηνίου και του φόντου για μια καταχώρηση
Αναζητήσιμο
Διάκριση πεζών-κεφαλαίων
Συγχώνευση από …
@@ -645,4 +645,27 @@
ΚΕΦΑΛΑΙΑ ΓΡΑΜΜΑΤΑ
Διαχωριστής
Αριθμός χαρακτήρων: %1$d
+ Επιλέξτε ένα κλειδί υλικού.
+ Το XML είναι εσφαλμένο.
+ Ακυρώθηκε από τον χρήστη.
+ Απαιτείται πρόγραμμα οδήγησης για %1$s.
+ Το κλειδί υλικού δεν υποστηρίζεται.
+ Να θυμάται τα κλειδιά υλικού
+ Παρακολουθεί τα κλειδιά υλικού που χρησιμοποιούνται
+ Επιτρέψτε σε εφαρμογές τρίτων να καταγράφουν ή να λαμβάνουν στιγμιότυπα οθόνης της εφαρμογής
+ Αναμονή για το αίτημα πρόκλησης…
+ Αναμονή για το αίτημα πρόκλησης…
+ Πλαίσιο ελέγχου κλειδιού υλικού
+ Κλειδί υλικού
+ Δεν είναι δυνατή η λήψη της απάντησης από την πρόκληση.
+ Λειτουργία στιγμιότυπου οθόνης
+ Λειτουργία στιγμιότυπου οθόνης
+ Η πρόκληση έχει ήδη ζητηθεί
+ Η απάντηση έχει ήδη δοθεί.
+ Η θέση της βάσης δεδομένων είναι άγνωστη, η ενέργεια της βάσης δεδομένων δεν μπορεί να εκτελεστεί.
+ Δεν είναι δυνατή η συγχώνευση από μια βάση δεδομένων V1.
+ Το κλειδί δεν μπορεί να είναι κενό.
+ Κατεστραμμένο αρχείο.
+ <strong>Δεν ανακτώνται δεδομένα χρήστη</strong>, αυτή η εφαρμογή δεν συνδέεται με κανένα διακομιστή, λειτουργεί μόνο τοπικά και σέβεται πλήρως το απόρρητο των χρηστών.
+ Αυτόματη-Πληκτρολόγηση
\ No newline at end of file
diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml
index 8b481e44d..de147d5a9 100644
--- a/app/src/main/res/values-en-rGB/strings.xml
+++ b/app/src/main/res/values-en-rGB/strings.xml
@@ -20,5 +20,34 @@
\n
\nGroups (~folders) organise entries in your database.
The existing OTP type is not recognised by this form, its validation may no longer correctly generate the token.
- By buying the pro version, you will have access to this visual style and you will especially help the realisation of community projects.
+ By buying the pro version, you will have access to this <strong>visual style</strong> and you will especially help <strong>the realisation of community projects.</strong>
+ Key derivation function
+ Feedback
+ Homepage
+ Accept
+ Add entry
+ Edit entry
+ Add group
+ Master key
+ Security
+ Encryption
+ Contact
+ Contribution
+ password
+ Encryption
+ Timeout
+ database
+ App
+ Brackets
+ Extended ASCII
+ Allow
+ Background
+ Open file
+ Node children
+ Add node
+ Add entry
+ Add group
+ Add item
+ File info
+ Credentials info
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 17aa8fe1a..2770a8d3c 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -21,24 +21,24 @@
-->
Comentarios
Página de inicio
- Implementación en Android del gestor de contraseñas KeePass
+ Implementación para Android del gestor de contraseñas KeePass
Aceptar
Añadir entrada
Añadir grupo
Algoritmo de cifrado
Tiempo de espera superado
- Inactividad antes del bloqueo de aplicación
+ Tiempo de inactividad antes de bloquear la base de datos
Aplicación
Configuración de la aplicación
- Corchete
+ Corchetes
Se requiere un administrador de archivos que acepte la acciones de intención ACTION_CREATE_DOCUMENT y ACTION_OPEN_DOCUMENT para crear, abrir y guardar los archivos de la base de datos.
Portapapeles vaciado
Portapapeles caducado
- Duración del almacenaje en el portapapeles (si lo admite su dispositivo)
+ Duración del almacenamiento en el portapapeles (si el dispositivo lo admite)
Seleccionar para copiar %1$s al portapapeles
- Recuperando clave de BdD…
+ Recuperando clave de la base de datos…
Base de datos
- Descifrando el contenido de la base de datos…
+ Descifrando contenido de la base de datos…
Utilizar como base de datos por defecto
Dígitos
KeePassDX © %1$d Kunzisoft es de <strong>código abierto</strong> y <strong>sin publicidad</strong>.
@@ -46,11 +46,11 @@
Abrir base de datos existente
Accedido
Cancelar
- Anotaciones
+ Notas
Confirmar contraseña
Creado
Caduca
- Cerrojo
+ Archivo clave
Modificada
Contraseña
Guardar
@@ -59,38 +59,38 @@
Nombre de usuario
No se admite el cifrador de flujo Arcfour.
KeePassDX no puede manejar este URI.
- No se pudo crear el archivo
- No se pudo leer la base de datos.
- Asegúrese de que la ruta sea correcta.
+ No se ha podido crear el archivo
+ No se ha podido leer la base de datos.
+ Asegúrese de que la ruta es correcta.
Proporcione un nombre.
- Seleccione un archivo de clave.
+ Seleccione un archivo clave.
No hay memoria para cargar toda la base de datos.
- Debe seleccionar al menos un tipo de generación de contraseñas.
+ Debe seleccionarse al menos un tipo de generación de contraseñas.
Las contraseñas no coinciden.
- Pasadas demasiado grande. Establecido a 2147483648.
+ Rondas de transformación demasiado altas. Establecidas a a 2147483648.
Introduzca un número entero positivo en el campo \"Longitud\".
Explorador de archivos
Generar contraseña
Confirmar contraseña
Contraseña generada
- Nombre de grupo
- Archivo de clave
+ Nombre del grupo
+ Archivo clave
Longitud
Contraseña
Contraseña
- No se pudieron leer las credenciales.
- No se pudo reconocer el formato de la base de datos.
+ No se han podido leer las credenciales.
+ No se ha podido reconocer el formato de la base de datos.
Longitud
Tamaño de los elementos de la lista
- Tamaño del texto de la lista de grupo
+ Tamaño del texto en la lista de elementos
Cargando base de datos…
Minúsculas
Ocultar contraseñas
- Enmascarar contraseñas (***) de manera predeterminada
+ Ocultar contraseñas (***) por defecto
Acerca de
- Cambiar Contraseña Maestra
+ Cambiar contraseña maestra
Configuración
- Configuración de Base de datos
+ Configuración de base de datos
Eliminar
Donar
Editar
@@ -102,13 +102,13 @@
Ir a URL
Menos
Nunca
- Sin resultado de búsqueda
+ Sin resultados de búsqueda
Instale un navegador web para abrir esta URL.
Creando nueva base de datos…
Trabajando…
- Quitar
+ Eliminar
Raíz
- Pasadas de transformación
+ Rondas de transformación
Un alto número de pasadas de cifrado proporciona protección adicional contra ataques de fuerza bruta, pero puede ralentizar mucho el cargado y el guardado.
Guardando base de datos…
Espacio
@@ -133,28 +133,28 @@
Permitir
Error del portapapeles
Algunos dispositivos no permiten que las aplicaciones utilicen el portapapeles.
- No se pudo vaciar el portapapeles
+ No se ha podido vaciar el portapapeles
Cada cadena debe tener un nombre de campo.
El servicio de autocompletado no se ha podido habilitar.
Nombre del campo
Valor del campo
- No se pudo encontrar el archivo. Intente volver a abrirlo en el explorador de archivos.
+ No se ha podido encontrar el archivo. Intente volver a abrirlo en el explorador de archivos.
Algoritmo incorrecto.
- El archivo de clave está vacío.
+ El archivo clave está vacío.
Copia de %1$s
- Llenado de formulario
+ Rellenado de formularios
Protección
Protegida contra escritura
Dependiendo del administrador de archivos, puede que KeePassDX no permita escribir en su almacenamiento.
- Algoritmo de cifrado utilizado para todos los datos.
+ Algoritmo de cifrado de la base de datos usado para todos los datos
Para generar la clave del algoritmo de cifrado, la clave maestra se transforma mediante una función de derivación de claves con una sal aleatoria.
Uso de memoria
Cantidad de memoria que usará la función de derivación de clave.
Paralelismo
- Grado de paralelismo (p. ej. número de hilos) usados por la función de derivación de clave.
+ Grado de paralelismo (p. ej. número de hilos) usado por la función de derivación de clave.
Ordenar
Más bajo primero ↓
- Agrupar antes
+ Grupos antes
Papelera debajo
Título
Nombre de usuario
@@ -162,7 +162,7 @@
Modificación
Acceso
Atención
- No se pudieron encontrar los datos de entrada.
+ No se han podido encontrar los datos de la entrada.
Evite los caracteres de la contraseña fuera del formato de codificación de texto en el archivo de la base de datos (los caracteres no reconocidos se convierten a la misma letra).
¿Continuar sin la protección de desbloqueo de contraseña\?
¿Continuar sin clave de cifrado\?
@@ -197,7 +197,7 @@
Nombre del archivo
Ruta
Asignar una clave maestra
- Crear una base de datos nueva
+ Crear nueva base de datos
Uso de la papelera de reciclaje
Mueve los grupos y las entradas al grupo \"Papelera de reciclaje\" antes de eliminarlos
Tipografía del campo
@@ -243,9 +243,9 @@
Ordenar registros y grupos de acuerdo a parámetros específicos.
Participar
Participe para aumentar la estabilidad, la seguridad y agregar más funciones.
- A diferencia de muchas aplicaciones de gestión de contraseñas, esta no tiene publicidad, es libre, con licencia «copyleft» y no recopila datos personales en sus servidores, sin importar la versión que use.
+ A diferencia de muchas aplicaciones de gestión de contraseñas, esta <strong>no tiene publicidad</strong>, es <strong>libre, con licencia «copyleft»</strong> y no recopila datos personales en sus servidores, sin importar la versión que use.
Al comprar la versión pro, tendrá acceso al <strong>estilo visual </strong>y ayudará especialmente a <strong>la realización de proyectos comunitarios.</strong>
- Este estilo visual está disponible gracias a su generosidad.
+ Este <strong>estilo visual</strong> está disponible gracias a su generosidad.
Para mantener nuestra libertad y estar siempre vigente, contamos con tu <strong>contribución.</strong>
Esta función está <strong>en desarrollo</strong> y requiere de tu <strong>contribución</strong> para estar disponible dentro de poco.
Al comprar la versión <strong>pro</strong>,
@@ -262,8 +262,8 @@
Cambiar el paquete de iconos en la aplicación
Editar entrada
No se pudo cargar la base de datos.
- No se pudo cargar la clave. Intente disminuir el uso de memoria de KDF.
- Enseña nombres de usuario
+ No se ha podido cargar la clave. Intente disminuir el uso de memoria de KDF.
+ Mostrar nombres de usuario
Muestra los nombres de usuario en las listas de entrada
Copiar
Mover
@@ -312,25 +312,25 @@
Abrir archivo
Nodo heredado
Añadir nodo
- Añadir apunte
+ Añadir entrada
Añadir grupo
- Informe de archivo
+ Información del archivo
Casilla de contraseña
- Casilla del cerrojo
- Icono de apunte
+ Casilla del archivo clave
+ Icono de entrada
Generador de contraseñas
Longitud de contraseña
Añadir campo
- Retirar campo
+ Eliminar campo
UUID
- No se puede mover una entrada aquí.
+ No puede mover una entrada aquí.
No puede copiar una entrada aquí.
- Mostrar el número de entradas
+ Mostrar número de entradas
Muestra el número de entradas de un grupo
Fondo
Actualizar
Cerrar campos
- No se puede crear la base de datos con esta contraseña y este archivo de clave.
+ No se puede crear la base de datos con esta contraseña y este archivo clave.
Desbloqueo avanzado
Biometría
Abrir petición automáticamente
@@ -346,7 +346,7 @@
Clave maestra
Seguridad
Historial
- Establecer una contraseña de un solo uso
+ Establecer contraseña de un solo uso
Tipo de contraseña de un solo uso
Secreto
Período (segundos)
@@ -355,13 +355,13 @@
Algoritmo
Contraseña de un solo uso
Secreto de contraseña de un solo uso inválida.
- Por lo menos un credencial debe ser definido.
- Clave secreta debe estar en formato Base32.
- Contador debe estar entre %1$d y %2$d.
+ Se debe establecer al menos una credencial.
+ La clave secreta debe estar en formato Base32.
+ El contador debe estar entre %1$d y %2$d.
No se puede guardar la base de datos.
Este texto no coincide con el elemento requerido.
- No fue posible crear el archivo de base de datos.
- Parar lograr <strong>mantener nuestra libertad</strong>, <strong>corregir errores</strong>, <strong>agregar características</strong> y <strong>estar siempre activos</strong>, contamos con tu <strong>contribución</strong>.
+ No se puede crear el archivo de la base de datos.
+ Parar lograr <strong>mantener nuestra libertad</strong>, <strong>corregir errores</strong>, <strong>añadir funciones</strong> y <strong>estar siempre activos</strong>, contamos con tu <strong>contribución</strong>.
Añadir elemento
¡Completado!
Finalizando…
@@ -391,17 +391,17 @@
Otorga acceso de escritura para guardar cambios en la base de datos
Mostrar ubicaciones de bases de datos recientes
Mostrar archivos recientes
- Lleva un registro de los lugares donde se almacenan las bases de datos
- Recordar las ubicaciones de las bases de datos
+ Lleva un registro de dónde se almacenan las bases de datos
+ Recordar ubicaciones de bases de datos
La base de datos contiene UUIDs duplicados.
Restaurar historial
Vaciar papelera de reciclaje
Guardar datos
Creando base de datos…
- %1$s con la misma UUID %2$s ya existe.
+ %1$s con el mismo UUID %2$s ya existe.
Esta etiqueta ya existe.
Descartar
- ¿Descartar los cambios\?
+ ¿Descartar cambios\?
Validar
Contribución
Contacto
@@ -417,18 +417,18 @@
El acceso al archivo fue revocado por el administrador de archivos
Ejecutando el comando…
Ocultar enlaces rotos en la lista de bases de datos recientes
- Ocultar enlaces de bases de datos rotos
- Realiza un seguimiento de dónde se almacenan los archivos de claves
- Recordar las ubicaciones de los archivos de clave
+ Ocultar enlaces rotos de la base de datos
+ Lleva un registro de dónde se almacenan los archivos clave
+ Recordar ubicaciones de archivos clave
Buscar dominios web con restricciones de subdominios
Búsqueda de subdominio
Solicite una búsqueda al abrir una base de datos
Búsqueda rápida
- Eliminar el historial
+ Eliminar historial
El token debe contener de %1$d a %2$d dígitos.
Adjuntos
Añadir adjunto
- Informe de credenciales
+ Información de credenciales
Base de datos abierta
Adjuntar
Cargue un archivo adjunto a la entrada para guardar datos externos importantes.
@@ -474,7 +474,7 @@
Modo de guardado
Modo de búsqueda
¿Resolver el problema generando nuevos UUID para que los duplicados continúen\?
- Borrar la clave de desbloqueo avanzado
+ Eliminar clave de desbloqueo avanzado
El nombre del campo ya existe.
Guardar un nuevo elemento no está permitido en una base de datos de sólo lectura
Recomendar la renovación
@@ -499,7 +499,7 @@
Lista de bloqueo de las aplicaciones
Solicitar datos de guardado al completar el llenado de un formulario
Pedir que se guarden los datos
- Intente guardar la información de la búsqueda cuando haga una selección de entrada manual
+ Trate de guardar la información de búsqueda al hacer una selección de entrada manual para facilitar los usos futuros
Guardar la información de la búsqueda
Cerrar la base de datos después de una selección de autocompletado
Cerrar la base de datos
@@ -515,12 +515,12 @@
Cambiar automáticamente al teclado anterior en la pantalla de credenciales de la base de datos
Pantalla de credenciales de la base de datos
Acción de la tecla automática
- Tras compartir informe a KeePassDX, cuando esté seleccionado un apunte, intente guardar el informe dentro del apunte para posibles futuros usos
+ Intente guardar la información compartida para su uso en el futuro cuando seleccione entradas manualmente
Guardar información compartida
Muestra el UUID vinculado a una entrada o a un grupo
Mostrar UUID
- No es posible reconstruir adecuadamente la lista.
- La URI de la base de datos no puede ser recuperada.
+ No se puede reconstruir correctamente la lista.
+ No se puede recuperar la URI de la base de datos.
Intenta mostrar sugerencias de autocompletado directamente desde un teclado compatible
Añadidas sugerencias de autocompletado.
Acceso al archivo revocado por el administrador de archivos, cierra la base de datos y vuelva a abrirla desde su ubicación.
@@ -535,9 +535,9 @@
El tipo de OTP existente no es reconocido por este formulario, su validación ya no puede generar correctamente el token.
¡Cancelado!
Los datos del archivo ya existen.
- Se produjo un error al cargar los datos del archivo.
+ Se ha producido un error al cargar los datos del archivo.
Propiedades de KeePassDX para gestionar la configuración de la aplicación
- Informe de contraseña de un solo uso
+ Información de contraseña de un solo uso
Personalizado
Estándar
Seleccionar temas oscuros o claros
@@ -551,9 +551,9 @@
Exportar propiedades de la aplicación
Seleccione un archivo para importar las propiedades de la aplicación
Importar propiedades de la aplicación
- Ocurrió un error al realizar una acción en la base de datos.
- Ocurrió un error al eliminar los datos del archivo.
- El archivo que está tratando de cargar es demasiado grande.
+ Se ha producido un error al realizar una acción en la base de datos.
+ Se ha producido un error al eliminar los datos del archivo.
+ El archivo que está intentando cargar es demasiado grande.
No puede mover un grupo aquí.
Esta palabra está reservada y no se puede usar.
Plantillas
@@ -564,14 +564,14 @@
Plantilla
Estándar
Membresía
- Nota Segura
+ Nota segura
Nombre del banco
Banco
Cuenta
Semilla
Clave privada
Clave pública
- Billetera de criptomonedas
+ Cartera de criptomonedas
Tipo
Wi-Fi
Dirección de correo electrónico
@@ -579,16 +579,16 @@
Fecha de emisión
Lugar de emisión
Nombre
- Tarjeta de Identificación
+ Documento de identidad
Número
Titular
Tarjeta de Crédito / Débito
Plantillas
- Número Internacional de Cuenta Bancaria
+ IBAN
SWIFT / BIC
Token
SSID
- Número de Identificación Personal
+ PIN
CVV
Muestra los tokens OTP en la lista de entradas
Mostrar token OTP
@@ -600,14 +600,16 @@
No ha permitido que la app use una alarma exacta. Como resultado, las funciones que requieren un temporizador no se harán con una hora exacta.
Permiso
Color de la base de datos
- Color de primer plano del apunte
- Color de fondo del apunte
+ Color de primer plano de la entrada
+ Color de fondo de la entrada
Etiquetas
La recarga de la base de datos borrará los datos modificados localmente.
El hash del archivo no está garantizado porque Android puede cambiar sus datos sobre la marcha. Cambia la extensión del archivo a .bin para una correcta integridad.
Mantener la pantalla encendida
Mantenga la pantalla encendida cuando vea la entrada
- Muestra los colores de primer y segundo plano en una entrada
+ Modo captura de pantalla
+ Permitir que otras aplicaciones graben o tomen capturas de pantalla de la aplicación
+ Muestra los colores de primer y segundo plano de una entrada
Colores de entrada
Fusionar datos
Cajón de navegación cerrado
@@ -615,16 +617,16 @@
Expresión regular
Cabecera de navegación
Cajón de navegación abierto
- Sensible a las mayúsculas y minúsculas
- Secuencia de autotipado
+ Sensible a mayúsculas y minúsculas
+ Secuencia de autocompletado
Datos personalizados
Grupo actual
Filtros de búsqueda
Fusionar desde …
- Guardar una copia en …
+ Guardar una copia en…
Heredar
Caducada
- Colorear caracteres de contraseña por el tipo
+ Colorear caracteres de contraseña por tipo
Ya hay abierta una base de datos, cierrela antes de abrir otra
Excluya caracteres ambiguos
Considere caracteres
@@ -634,8 +636,8 @@
\n
\nDependiendo de la implementación del API nativo del sistema operativo, tal vez no sea completamente funcional.
\nCompruebe la compatibilidad y garantía del almacenaje KeyStore con el fabricante de su dispositivo y el creadas de la ROM que está utilizando.
- Conteo de palabras de contraseña-frase
- Contraseña frase
+ Recuento de palabras de la frase de paso
+ Frase de acceso
Colorear contraseñas
Pantalla de búsqueda
Volver automáticamente al teclado anterior en la pantalla de búsqueda
@@ -647,4 +649,25 @@
MAYÚSCULAS
Tipo Titular
Conteo de caracteres: %1$d
+ Modo captura de pantalla
+ La llave física no está soportada.
+ <strong>No se recuperan datos del usuario</strong>, esta aplicación no se conecta a ningún servidor, funciona solo localmente y respeta completamente la privacidad del usuario.
+ Imposible fusionar desde una base de datos V1.
+ Cancelado por el usuario.
+ Imposible obtener la respuesta del desafío.
+ Autocompletar
+ Llave física
+ Seleccione una llave física.
+ Error en la sintaxis XML.
+ Desafío ya solicitado
+ Respuesta ya recibida.
+ Se necesita un controlador para %1$s.
+ La ubicación de la base de datos es desconocida, la acción de la base de datos no se puede realizar.
+ La clave no puede estar vacía.
+ Archivo dañado.
+ Recordar llaves físicas
+ Realiza un seguimiento de las llaves físicas usadas
+ Casilla de llave física
+ Esperando solicitud de desafío…
+ Esperando respuesta al desafío…
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index cd6136647..b55e1fb32 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -126,7 +126,7 @@
Selon votre gestionnaire de fichiers, KeePassDX peut ne pas être autorisé à écrire dans votre stockage.
Supprimer
Racine
- Algorithme de chiffrement de la base de données utilisé pour toutes les données.
+ Algorithme de chiffrement de la base de données utilisé pour toutes les données
Afin de générer la clé pour l’algorithme de chiffrement, la clé principale est transformée en utilisant une fonction de dérivation de clé salée aléatoirement.
Tours de transformation
Des tours de chiffrement supplémentaires fournissent une protection plus élevée contre les attaques par force brute, mais cela peut considérablement ralentir les opérations de chargement et d’enregistrement.
@@ -290,7 +290,7 @@
\n\"Protéger en écriture\" empêche les modifications involontaires de la base de données.
\n\"Modifiable\" vous permet d’ajouter, de supprimer ou de modifier tous les éléments comme vous le souhaitez.
Modifier l’entrée
- Impossible de charger votre base de données.
+ Impossible de charger la base de données.
Impossible de charger la clé. Essayez de diminuer l’utilisation mémoire de la fonction de dérivation de clé.
Afficher les noms d’utilisateur
Affiche les noms d’utilisateur dans les listes d’entrées
@@ -319,7 +319,7 @@
Action de touche automatique
Revenir automatiquement au clavier précédent après avoir exécuté \"Action de touche automatique\"
Mode sélection
- Veuillez ne pas tuer l’application…
+ Ne pas tuer l\'application…
Appuyer sur \"Retour\" pour verrouiller
Verrouille la base de données lorsque l’utilisateur clique sur le bouton retour de l’écran racine
Suppression à la fermeture
@@ -492,7 +492,7 @@
Fermer la base de données
Revient automatiquement au clavier précédent après le verrouillage de la base de données
Verrouiller la base de données
- Essaye de sauvegarder les informations partagées lors de la sélection manuelle d\'une entrée pour faciliter les utilisations futures.
+ Essaye de sauvegarder les informations partagées lors de la sélection manuelle d\'une entrée pour faciliter les utilisations futures
Enregistrer les infos partagées
Notification
Mise à jour de sécurité biométrique requise.
@@ -614,7 +614,7 @@
Maintiens l\'écran allumé lorsque l\'entrée est visionnée
Le hachage du fichier n\'est pas garanti car Android peut modifier ses données à la volée. Changez l\'extension du fichier en .bin pour une intégrité correcte.
Maintenir l\'écran allumé
- Affiche les couleurs de premier plan et d\'arrière-plan dans une entrée
+ Affiche les couleurs de premier plan et d\'arrière-plan pour une entrée
Le rechargement de la base de données supprimera les données modifiées localement.
Couleurs d\'entrée
Groupe actuel
@@ -655,4 +655,27 @@
Considérer les caractères
Séparateur
Nombre de caractères : %1$d
+ Auto-Type
+ La clé ne peut pas être vide.
+ Mémoriser les clés matérielles
+ <strong>Aucune donnée utilisateur n\'est récupérée</strong>, cette application ne se connecte à aucun serveur, ne fonctionne que localement et respecte totalement la vie privée des utilisateurs.
+ Mode capture d\'écran
+ Clé matérielle
+ L\'emplacement de la base de données est inconnu, l\'action sur la base de données ne peut pas être exécutée.
+ Autoriser les applications tierces à enregistrer ou à faire des captures d\'écran de l\'application
+ Sélectionnez une clé matérielle.
+ XML malformé.
+ La clé matérielle n\'est pas prise en charge.
+ Fichier corrompu.
+ Conserve la trace des clés matérielles utilisées
+ Mode capture d\'écran
+ Case à cocher de clé matérielle
+ En attente de la requête de challenge…
+ En attente de la réponse de challenge…
+ Challenge déjà demandé
+ Réponse déjà fournie.
+ Impossible d\'obtenir la réponse du challenge.
+ Annulé par l\'utilisateur.
+ Le pilote pour %1$s est nécessaire.
+ Impossible de fusionner à partir d\'une base de données V1.
\ No newline at end of file
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index d5db3826d..67f76acf1 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -19,14 +19,14 @@
-->
Comentarios
Páxina inicial
- KeePassDX é unha implementación para Android do xestor de contrasinais KeePass.
+ KeePassDX é unha implementación para Android do xestor de contrasinais KeePass
Aceptar
Engadir entrada
Engadir grupo
Cifrado
Algoritmo de cifrado
Función de derivación de chave
- Tempo de espera da aplicación
+ Tempo de espera
Tiempo de inactividad antes de bloquear la base de datos
Aplicación
Parénteses
@@ -34,9 +34,9 @@
Permitir
Portapapeis limpo
Erro do portapapeis
- Fallou a limpeza do portapapeis
+ Non foi posíbel limpar o portapapeis
Tempo límite para o portapapeis
- Tempo antes de limpar o portapapeis após copiar usuario ou contrasinal
+ Duración do almacenamento do portapapeis (se for soportado polo seu dispositivo)
Editar entrada
Seleccione para copiar %1$s para o portapapeis
A criar a chave da base de dados…
@@ -54,4 +54,613 @@
Nome
URL
Usuario
+ Un xestor de ficheiros que acepte a acción intencional ACTION_CREATE_DOCUMENT e ACTION_OPEN_DOCUMENT é necesario para crear, abrir e gravar ficheiros da base de datos.
+ Expirou
+ Non foi posíbel atopar os datos da entrada.
+ Período (segundos)
+ Contador
+ Díxitos
+ Filtros de procura
+ Algoritmo
+ Enderezo de email
+ SSID
+ Tipo
+ Banco
+ Segredo OTP inválido.
+ As contrasinais non coinciden.
+ \"Rondas de transformación\" moi grandes. Alterado para 2147483648.
+ O cifrado de fluxo Arcfour non é soportado.
+ Non pode copiar un grupo aquí.
+ Non foi posíbel crear o ficheiro da base de datos.
+ Nome do banco
+ Non foi posíbel crear o ficheiro
+ Introduza un nome.
+ Ten que seleccionar polo menos un tipo de xeración de contrasinal.
+ Non foi posíbel abrir a base de datos.
+ Ten que definir polo menos unha credencial.
+ Non foi posíbel procesar esta URI no KeePassDX.
+ Non foi posíbel ler a base de datos.
+ Non pode mover unha entrada aquí.
+ Cor da base de datos
+ Cor do primeiro plano da páxina inicial
+ Número
+ Email
+ Non foi posíbel carregar a clave. Tente diminuír o \"Uso de memoria\" do KDF.
+ Información de contrasinal de uso único
+ Actualizar
+ Remover
+ Pechar campos
+ Engadir elemento
+ Clave mestra
+ Seguranza
+ Histórico
+ Anexos
+ Sen memoria suficiente para carregar toda a base de datos.
+ Utilizar como base de datos predefinida
+ Icono da entrada
+ Repetir alternar a visibilidade do contrasinal
+ Caixa de selección do ficheiro-clave
+ Información sobre credenciais
+ Caixa de selección do contrasinal
+ Información sobre o ficheiro
+ Abrir ficheiro
+ Algúns dispositivos non permiten que as aplicacións usen o portapapeis.
+ Contribución
+ Contacto
+ Validar
+ Descartar
+ Descartar cambios\?
+ Longitude de contrasinal
+ Engadir campo
+ Engadir anexo
+ A descifrar o contido da base de datos…
+ Remover campo
+ Modelos
+ KeePassDX © %1$d Kunzisoft ten o <strong>código fonte aberto</strong> e <strong>sen publicidade</strong>.
+\nÉ provisto como está, baixo a <strong>licenza GPLv3</strong>, sen ningunha garantía.
+ UUID
+ Ficheiro-clave
+ Procurábel
+ Herdar
+ Etiquetas
+ Estabelecer contrasinal de uso único
+ Grupo actual
+ Sensíbel a maiúsculas e minúsculas
+ Expresión regular
+ Cartón de Crédito / Débito
+ Titular
+ CVV
+ PIN
+ Cartón de identificación
+ Nome
+ Lugar de emisión
+ Data de emisión
+ Clave pública
+ Clave privada
+ Semente
+ Conta
+ Non pode mover un grupo aquí.
+ Non pode copiar unha entrada aquí.
+ Engadir grupo
+ Nó fillo
+ Versión
+ Engadir nó
+ Engadir entrada
+ Xerador de contrasinais
+ Cor do fondo da páxina inicial
+ Tipo do OTP
+ IBAN
+ Estándar
+ Número de palabras do frase-sinal
+ Datos personalizados
+ Para <strong>manter a liberdade</strong>, <strong>corrixir erros</strong>, <strong>engadir funcionalidades</strong> e <strong>para estar sempre activos</strong>, contamos coa súa <strong>contribución</strong>.
+ Segredo
+ Carteira de criptomoedas
+ Token
+ SWIFT / BIC
+ Wi-Fi
+ Modelo
+ Nota segura
+ Certifique que a ruta é válida.
+ Filiación
+ OTP
+ Esta palabra está reservada e non pode ser utilizada.
+ Esta etiqueta xa existe.
+ O contador debe estar entre %1$d e %2$d.
+ Caixa de verificación da clave hardware
+ A agardar polo pedido do desafío…
+ XML malformado.
+ Á espera da resposta ao desafío…
+ Secuencia de autocomplección
+ Seleccione un ficheiro clave.
+ Clave hardware
+ Os datos do ficheiro xa existen.
+ Seleccione unha clave hardware.
+ Un erro aconteceu ao tentar enviar os datos do ficheiro.
+ Copiar
+ O arquivo que está a tentar enviar é demasiado grande.
+ Gardar unha copia en …
+ Protección contra escritura
+ Modificábel
+ A clave hardware non é soportada.
+ Valor do campo
+ Confirmar contrasinal
+ Ficheiro corrompido.
+ Borrar a clave do desbloqueo avanzado
+ A carregar base de datos…
+ Mascarar contrasinais (***) por predefinición
+ Tamaño do texto na lista de elementos
+ A crear a base de datos…
+ Mover
+ Combinar desde …
+ Pegar
+ Configuracións da base de datos
+ Restaurar historial
+ Procurar
+ Mostrar contrasinal
+ Baleirar a papeleira de reciclaxe
+ Borrar historial
+ Ir ao URL
+ Icono externo
+ Menos
+ Introduza un número enteiro positivo no campo \"Lonxitude\".
+ Un erro ocorreu mentres os datos do ficheiro eran borrados.
+ Frase-clave
+ Colorear contrasinais
+ Colorear caracteres dos contrasinais por tipo
+ Configuracións
+ Ocultar contrasinal
+ Bloquear base de datos
+ Gardar datos
+ Sen resultados da procura
+ Instale un navegador web para abrir este URL.
+ O nome do campo xa existe.
+ O URI da base de datos non pode ser recuperada.
+ Contrasinal
+ Algoritmo errado.
+ %1$s co mesmo UUID %2$s xa existe.
+ Desbloqueo avanzado
+ A clave secreta debe estar en formato Base32.
+ Lonxitude
+ Cambiar clave mestra
+ Configuracións de seguranza
+ Nunca
+ Este texto non corresponde ao elemento solicitado.
+ Mostra nomes do usuario nas listas de entrada
+ Mostrar token OTP
+ Mostrar número de entradas
+ Mostra os tokens OTP na lista de entradas
+ <strong>Datos do usuario non recuperados </strong>, esta aplicación non se conecta a ningún servidor, só opera localmente e respecta totalmente a privacidade dos usuarios.
+ Non foi posíbel crear a base de datos con ese contrasinal e ficheiro clave.
+ Non foi posíbel gardar a base de datos.
+ Non é posíbel reconstruír a lista adecuadamente.
+ Desafío xa solicitado
+ Resposta xa obtida.
+ Non foi posíbel obter a resposta do desafío.
+ Cancelado polo usuario.
+ É necesario o driver para %1$s.
+ Non foi posíbel combinar desde a base de datos V1.
+ A localización da base de datos é descoñecida, a acción non pode ser executada.
+ A clave non pode estar baleira.
+ Xestor de ficheiros
+ Xerar contrasinal
+ Contrasinal xerado
+ Non foi posíbel ler as credenciais.
+ Non foi posíbel recoñecer o formato da base de datos.
+ O ficheiro clave está baleiro.
+ Lonxitude
+ Mostrar UUID
+ Mostrar o UUID ligado a unha entrada do grupo
+ Mostra o número de entradas dun grupo
+ Tamaño da lista de elementos
+ Doar
+ Cancelar
+ Combinar datos
+ Abrir
+ Configuracións da clave mestra
+ Recarregar datos
+ Borrar
+ Cada cadea debe ter un nome de campo.
+ Fondo
+ Caixón de navegación aberto
+ Cabeceira de navegación
+ Caixón de navegación fechado
+ Non foi posíbel habilitar o servizo de autocompletado.
+ Un erro ocorreu mentres se realizaba unha acción na base de datos.
+ O período debe estar entre %1$d e %2$d segundos.
+ O token debe ter entre %1$d e %2$d díxitos.
+ O tipo de OTP existente non é recoñecido por este formulario, é posíbel que a súa validación non poida xerar o token correctamente.
+ Nome do campo
+ Gardar un novo elemento non é permitido nunha base de datos de só lectura
+ Non foi posíbel atopar o ficheiro. Probe a volver abrilo no seu explorador de ficheiros.
+ Nome do grupo
+ Nome do icono
+ Mostrar nomes de usuario
+ Ficheiro clave
+ Contrasinal
+ Ocultar contrasinais
+ Minúsculas
+ Sobre
+ Copia de %1$s
+ Configuracións da aplicación
+ Editar
+ Autocompletar
+ Abrir base de datos existente
+ Solicitar unha procura cando abrir a base de datos
+ Propiedades da aplicación importadas
+ Ocultar ligazóns rotas das bases de datos
+ Raíz
+ Continuar sen clave de cifrado\?
+ A crear nova base de datos…
+ Traballando…
+ A executar o comando…
+ Espazo
+ Ordenar
+ A gardar base de datos…
+ Procurar dominios web con restricións de subdominios
+ Modo de gardado
+ Protección
+ Procurar subdominios
+ Protección contra escritura
+ Motor de procura
+ Importar propiedades da aplicación
+ Mantén un rexistro de onde son almacenadas as bases de datos
+ Mostrar localizacións das bases de datos recentes
+ Lembrar localizacións dos ficheiros clave
+ Maiúsculas
+ Advertencia
+ Propiedades da aplicación exportadas
+ Nome de usuario
+ Creación
+ Paralelismo
+ Cantidade de memoria a ser usada pola función de derivación de clave.
+ Versión da base de datos non soportada
+ Unha base de datos de KeePass debe conter só pequenos ficheiros utilitarios (como ficheiros clave PGP).
+\n
+\nA súa base de datos pode acabar sendo moi grande e reducir o rendemento.
+ Algoritmo de cifrado da base de datos usado para todos os datos
+ Crear base de datos
+ Dependendo do xestor de ficheiros, KeePassDX pode non ter permiso para escribir no almacenamento.
+ A base de datos contén UUIDs duplicados.
+ Resolver o problema xerando novas UUIDs para que os duplicados continúen\?
+ Modo de selección
+ Modo de rexistro
+ Lembrar localización das bases de datos
+ Mantén un rexistro de onde son almacenados os ficheiros clave
+ Lembrar claves hardware
+ Mantén un rexistro das claves hardware usadas
+ Mostrar ficheiros recentes
+ Ocultar ligazóns rotas na lista de bases de datos recentes
+ Erro ao importar as propiedades da aplicación
+ Erro ao exportar as propiedades da aplicación
+ Para xerar a clave do algoritmo de cifrado, a clave mestra é transformada usando unha función de derivación de clave cun salto aleatorio.
+ Rondas de transformación
+ Rondas de cifrado adicionais fornecen máis protección contra ataques por forza bruta, mais poden demorar a carga e o gardado.
+ Uso de memoria
+ Grao de paralelismo (p. ex. número de fíos) usados pola función de derivación de clave.
+ Non feche a aplicación…
+ Filtro
+ Mais baixo primeiro ↓
+ Grupos primeiro
+ Papeleira de reciclaxe ao fondo
+ Orde natural
+ Título
+ Modificación
+ Procura
+ Subliñado
+ Xa está aberta unha base de datos, féchea primeiro para abrir unha nova
+ Continuar sen protección de desbloqueo por contrasinal\?
+ Borrar os nós seleccionados permanentemente\?
+ Borrar todos os nós da papeleira de reciclaxe permanentemente\?
+ O ficheiro enviado substituirá o existente.
+ Engadir o ficheiro igualmente\?
+ Seleccione un arquivo para importar as propiedades da aplicación
+ Propiedades do KeePassDX para xestionar as configuracións da aplicación
+ Exportar as propiedades da aplicación
+ Crear un ficheiro para exportar as propiedades da aplicación
+ Garanta acceso de escritura para gardar os cambios na base de datos
+ Especial
+ O acceso ao arquivo foi revocado polo xestor de ficheiros
+ Acceso
+ Evite para o contrasinal caracteres fóra do formato de codificación do ficheiro da base de datos (todos os caracteres non recoñecidos son convertidos na mesma letra).
+ Procura rápida
+ Contido
+ Permiso
+ Abrir o aviso de desbloqueo avanzado para acceder á base de datos
+ Configuracións de autocompletado
+ Recoñecemento de desbloqueo avanzado
+ É necesaria unha actualización da seguranza biométrica.
+ Abrir a base de datos co recoñecemento de desbloqueo avanzado
+ Biométrico
+ Habilite o servizo para autocompletar rapidamente os formularios de outras aplicacións
+ Notificacións do portapapeis
+ Estabelecer o servizo de autocompletado predefinido
+ Bloquear a base de datos cando o usuario faga click no botón de volver á pantalla principal
+ Mostrar botón de bloqueo
+ Mostrar notificacións do portapapeis ao visualizar unha entrada
+ Tocar para borrar as claves de desbloqueo avanzado
+ Permitelle usar as credenciais do dispositivo para abrir a base de datos
+ Desbloqueo coas credenciais do dispositivo
+ Bloquear a base de datos após uns segundos da pantalla apagar
+ Borrar datos non ligados pode reducir o tamaño da súa base de datos, mais tamén pode eliminar datos usados polos plugins do KeePass.
+ Borrar estes datos igualmente\?
+ Non é recomendábel engadir un ficheiro clave baleiro.
+ O contido do arquivo clave non deber ser modificado nunca, e no mellor dos casos, debería conter datos xerados aleatoriamente.
+ A información contida no seu ficheiro de base de datos foi modificada fóra da aplicación.
+ Combinar os datos, sobrescribir as modificacións externas gardando a base de datos ou recarregala cos últimos cambios.
+ Recarregar a base de datos borrará os datos modificados localmente.
+ Acceso ao ficheiro revocado polo xestor de ficheiros, feche a base de datos e ábraa dende a súa localización.
+ Non permitiu o uso dunha alarma exacta pola aplicación. Como resultado, as características que requeren un temporizador non empregaran o tempo exacto.
+ O hash do ficheiro non é garantido porque o Android pode cambiar os datos sobre a marcha. Cambie a extensión a .bin para garatir a integridade.
+ Versión %1$s
+ Compilación %1$s
+ Non hai rexistro de ningún credencial biométrico ou de dispositivo.
+ O almacén de claves non foi iniciado correctamente.
+ Abrir o aviso de desbloqueo avanzado para gardar as credenciais
+ Extraer a credencial da base de datos cos datos de desbloqueo avanzado
+ Esta base de datos non ten credenciais almacenadas aínda.
+ Non foi posíbel inciar o desbloqueo avanzado.
+ Escriba o contrasinal, e logo faga click neste botón.
+ Propiedades
+ Aparencia
+ Credenciais do dispositivo
+ Xeral
+ Autocompletado
+ Usar o desbloqueo avanzado para abrir a base de datos dun xeito máis sinxelo
+ Inciar sesión con KeePassDX
+ Seleccione entrada…
+ Tamaño do contrasinal xerado
+ Estabelece o tamaño predefinido dos contrasinais xerados
+ Caracteres do contrasinal
+ Estabelecer os caracteres permitidos no xerador de contrasinais
+ Base de datos aberta
+ Portapapeis
+ Copiar campos de entrada usando o portapapeis do seu dispositivo
+ Se a limpeza do portapapeis fallar, borre o historial manualmente.
+ Bloquear
+ Bloqueo de pantalla
+ Presione \'Atrás\' para bloquear
+ Mostrar o botón de bloqueo na interface do usuario
+ Desbloqueo avanzado
+ Desbloqueo biométrico
+ Permite o escaneo dos datos biomérifos para abrir a base de datos
+ Non foi posíbel ler a clave de desbloqueo avanzado. Por favor, bórrea e repita o procedemento de recoñecemento do desbloqueo.
+ Contrasinal cifrado almacenado
+ Non foi posíbel recoñecer a pegada do desbloqueo avanzado
+ Erro de desbloqueo avanzado: %1$s
+ Servizo de autocompletado do KeePassDX
+ Historial
+ Aínda precisa lembrar a súa credencial principal se usar o recoñecemento de desbloqueo avanzado.
+ Desbloqueo avanzado temporal
+ Nome do arquivo
+ Duración do desbloqueo avanzado
+ Duración do uso do desbloqueo avanzado antes de borrar os seus contidos
+ Tempo límite de desbloqueo avanzado
+ Borrar todas as claves de cifrado relacionadas co desbloqueo de recoñecemento avanzado
+ Asignar unha clave mestra
+ Datos
+ A compresión de datos reduce o tamaño da base de datos
+ Borrar datos non ligados
+ Borrar anexos contidos na base de datos mais non ligados a unha entrada
+ Non foi posíbel iniciar esta funcionalidade.
+ Compresión de datos
+ Pedir automaticamente o desbloqueo avanzado se a base de datos estiver configurada para o seu uso
+ Non almacenar ningún contido cifrado para o desbloqueo avanzado
+ Borrar todas as claves de cifrado relacionadas co desbloqueo de recoñecemento avanzado\?
+ Esta funcionalidade almacenará os datos de credenciais cifrados na KeyStore do seu dispositivo.
+\n
+\nDependendo da implementación da API nativa do seu sistema operativo, pode non ser completamente funcional.
+\nRevise a compatibilidade e seguranza da KeyStore co fabricante do seu dispositivo e co creador da ROM que está a usar.
+ Este dispositivo está a usar Android %1$s, mais precisa do %2$s ou posterior.
+ Borrar claves de cifrado
+ Non foi posíbel encontrar o hardware correspondente.
+ Ruta
+ Renovación recomendada
+ Notificación
+ Limpar ao fechar
+ Cor personalizada da base de datos
+ Modelos
+ Teclado
+ Aparencia
+ Son ao presionar teclas
+ %1$s
+ Mudar de teclado
+ Retroceder
+ Acción da tecla automática
+ Fechar base de datos
+ Selección manual
+ Configuración do teclado do dispositivo
+ Active un teclado personalizado para os seus contrasinais e todos os campos de identidade
+ Pantalla de credencias da base de datos
+ Reiniciar a aplicación que contén o formulario para activar o bloqueo.
+ Entrada
+ Tema do teclado
+ Teclas
+ Acción de tecla automática
+ Acción da tecla \"Ir\" após presionar unha tecla \"Campo\"
+ Pantalla de procura
+ Campos personalizados
+ Volver ao teclado anterior
+ Tentar mostrar suxestións de autocompletado directamente desde un teclado compatíbel
+ Mostrar opción para permitir ao usuario seleccionar a entrada da base de datos
+ Lista de bloqueo dos dominios web
+ Bloquear autocompletado
+ Confiar no portapapeis
+ Cambiar a fonte usada nos campos para mellorar a visibilidade dos caracteres
+ Permitir copiar o contrasinal e protexer os campos no portapapeis
+ Aviso: O portapapeis é compartillado con todas as aplicacións. Se datos sensíbeis foren copiados, outro programas poderían lelos.
+ Nome da base de datos
+ Descrición da base de datos
+ Tentar gardar información compartillada ao facer unha selección manual de entradas para facilitar usos futuros
+ Interface
+ Mover grupos e entradas na papeleira de reciclaxe antes de eliminalos
+ Uso da papeleira de reciclaxe
+ Grupo da papeleira de reciclaxe
+ Uso de modelos
+ Use modelos dinámicos para completar os campos dunha entrada
+ Número máximo
+ Limitar o número de elementos no historial por entrada
+ Tamaño máximo
+ Limitar o tamaño do historial por entrada
+ Recomendar cambiar a clave mestra (días)
+ Forzar renovación
+ Forzar renovación da clave mestra (días)
+ Forzar renovación a próxima vez
+ Forzar cambio da clave mestra a próxima vez (unha vez)
+ Tipo de letra dos campos
+ Habilitar
+ Deshabilitar
+ Bloquear a base de datos cando a duración do portapapeis expire ou cando a notificación for fechada despois de utilizala
+ Nome do usuario predefinido
+ Versión da base de datos
+ Texto
+ Outros
+ Compresión
+ Ningunha
+ Gzip
+ Papeleira de reciclaxe
+ Magikeyboard
+ Entrada
+ Selección da entrada
+ Información da notificación
+ Limpar ao fechar
+ Tempo límite
+ Tempo límite para limpar a entrada do teclado
+ Vibrar ao presionar teclas
+ Cambiar automaticamente ao teclado anterior na pantalla da base de datos
+ Bloquear base de datos
+ Seleccionar entrada
+ Fechar a base de datos após autocompletar a selección
+ Suxestións dentro da liña
+ Gardar información de procura
+ Gardar información compartillada
+ Mostrar unha notificación cando a entrada estiver dispoñíbel
+ Grupo de modelos
+ MagiKeyboard
+ Fechar a base de datos ao fechar a notificación
+ Cambiar automaticamente ao teclado previo na pantalla de procura
+ Tentar gardar informacións de procuras ao facer unha selección manual da entrada para usos futuros
+ Lista de bloqueo das aplicacións
+ Lista de bloqueo sen permiso para autocompletar automaticamente
+ Abra o seu arquivo de base de datos dende o seu explorador de ficheiros para usalo.
+ Os campos copiados poden ser pegados en calquer lado.
+\n
+\nUse un método de completado de formularios da súa escolla.
+ Bloquear base de datos
+ Ao ver unha entrada en KeePassDX, completar con esta o Magikeyboard
+ Mudar automaticamente ao teclado previo despois de bloquear a base de datos
+ Pedir para gardar datos
+ Non é posíbel gardar nunha base datos aberta en modo só lectura.
+ Pedir para gardar datos cando terminar de autocompletar un formulario
+ Borrar o contrasinal introducido após un intento de conexión a unha base de datos
+ Restabelecer suxestións educativas
+ Desbloqueo avanzado da base de datos
+ Ligue o seu contrasinal á súa credencial biométrica ou credenciais do dispositivo para desbloquear rapidamente a súa base de datos.
+ Gardar base de datos automaticamente
+ Manter a pantalla acesa
+ Permitir pulsar o botón \"Abrir\" se non seleccionar ningunha credencial
+ Só lectura
+ Destacar elementos para saber como funciona a aplicación
+ Suxestións educativas restabelecidas
+ Crear o teu arquivo de base de datos
+ Cree o seu primeiro arquivo de xestión de contrasinais.
+ Abrir unha base de datos existente
+ Engadir elementos á súa base de datos
+ As entradas axudan a xestionar as súas identidades dixitais.
+\n
+\nOs grupos (~cartafois) organizan entradas na súa base de datos.
+ Procurar nas entradas
+ Introduza título, nome de usuario ou outros campos para recuperar os seus contrasinais.
+ Editar a entrada
+ Rexistre un campo adicional, engada un valor e opcionalmente protéxao
+ Engadir un anexo
+ Configurar OTP
+ Introduza un contrasinal e/ou un arquivo para desbloquear a súa base de datos.
+\n
+\nFaga unha copia de seguranza da súa base de datos nun lugar seguro despois de cada cambio.
+ Protexa a base de datos contra escritura
+ Cambie o modo de apertura da sesión.
+\n
+\n\"Protección contra escritura\" evita cambios non desexados na base de datos.
+\n\"Modificábel\" permite engadir, borrar, ou modificar todos os elementos.
+ Copia un campo
+ Enter
+ Abrir petición automaticamente
+ Magikeyboard (KeePassDX)
+ Configuración do Magikeyboard
+ Mudar automaticamente para o teclado previo após executar \"Acción de tecla automática\"
+ Permitir ás aplicacións de terceiros gravar ou tomar capturas na aplicación
+ Suxestións educativas
+ Mostrar suxestións educativas outra vez
+ Crear un contrasinal forte
+ Engadir campos personalizados
+ Completado de formularios
+ %1$s dispoñíbel no Magikeyboard
+ Abrir a base de datos en modo de só lectura por defecto
+ Configure a xestión do contrasinal dun só uso (HOTP / TOTP) para xerar un token solicitado para a autenticación de dous factores (2FA).
+ Desbloquee a súa base de datos
+ Eliminar contrasinal
+ Engadidas suxestións de autocompletado.
+ Non permitir clave mestra
+ Gardar a base de datos após unha acción importante (en modo \"Modificábel\")
+ Manter a pantalla acesa ao ver unha entrada
+ Modo captura de pantalla
+ Ao mercar a versión pro, terá acceso ao <strong>estilo visual</strong> e axudará especialmente a <strong> realizar proxectos comunitarios.</strong>
+ Ao <strong>contribuír</strong>,
+ KiB
+ Completado!
+ Entropía: %1$s bit
+ Número de caracteres: %1$d
+ Capitalización do Título
+ Escolla temas claros ou escuros
+ Paquete de iconos
+ Mostrar cores de fondo e de primeiro plano nunha entrada
+ Estamos a traballar para lanzar esta nova funcionalidade o máis rápido posíbel.
+ Descarregar
+ Subir %1$s
+ B
+ MiB
+ GiB
+ Tema usado na aplicación
+ Brillo do tema
+ Entradas expiradas non son mostradas
+ Participe
+ Axude a aumentar a estabilidade, seguranza e número de funcionalidades.
+ Este <strong>estilo visual</strong> está dispoñíbel grazas á súa xenerosidade.
+ Para manter a nosa liberdade e estarmos sempre activos, contamos coa súa <strong>contribución.</strong>
+ Descarregar %1$s
+ A inicializar…
+ En progreso: %1$d%%
+ Entropía: A calcular…
+ Separador
+ Ignorar caracteres
+ minúsculas
+ MAIÚSCULAS
+ Cores da entrada
+ Bloquee a súa base de datos rapidamente. Pode configurar a aplicación para bloqueala despois dun tempo ou cando a pantalla se apague.
+ Ordenación de elementos
+ Escolla as entradas e grupos a ordenar.
+ Esta funcionalidade está <strong>en desenvolvemento</strong> e precisa da súa <strong>contribución</strong> para a súa futura dispoñibilidade.
+ Moitas grazas pola súa contribución.
+ Lembre manter a súa aplicación actualizada, instalando as novas versións.
+ Contribuír
+ Entropía: Alta
+ Polo menos un carácter de cada un
+ Excluír caracteres ambiguos
+ Modo captura de pantalla
+ Ocultar entradas expiradas
+ Tema da aplicación
+ A finalizar…
+ Estándar
+ Lista de dominios web bloqueados sen permiso de autocompletado
+ Cancelado!
+ Xere un contrasinal forte a asociar coa súa entrada, defínao facilmente segundo os criterios do formulario e non esqueza tornalo seguro.
+ Suba un anexo á súa entrada para gardar datos externos importantes.
+ Ao contrario de moitas aplicacións de xestión de contrasinais, esta aplicación <strong>non ten anuncios</strong>, é <strong>un programa de software libre \"copyleft\"</strong> e non recolle datos persoais para os seus servidores, sen importar a versión a usar.
+ Edite a entrada cos campos personalizados. Os conxuntos de datos poden ser referenciados en entradas diferentes.
+ Considerar caracteres
+ Personalizado
+ Cambiar o paquete de iconos da aplicación
+ Ao comprar a versión <strong>pro</strong>,
+ está a incentivar os programadores a crear <strong>novas funcionalidades</strong> e a <strong>corrixir erros</strong> reportados.
\ No newline at end of file
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index db25cdd54..6f4de2173 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -45,8 +45,8 @@
डेटाबेस सामग्री डिक्रिप्ट कर रहे है…
डिफ़ॉल्ट डेटाबेस के रूप में उपयोग करें
अंक
- KeePassDX © %1$d Kunzisoft ओपन सोर्स है और बिना किसी विज्ञापन के उपलब्ध है।
-\nइसे GPLv3 लाइसेंस के अन्तर्गत बिना किसी वारन्टि के प्रदान किया गया है।
+ KeePassDX © %1$d Kunzisoft <strong>ओपन सोर्स</strong> है और <strong>बिना किसी विज्ञापन</strong> के उपलब्ध है।
+\nइसे <strong>GPLv3</strong> लाइसेंस के अन्तर्गत बिना किसी वारन्टि के प्रदान किया गया है।
एक्सेस किया गया
रद्द करें
टिप्पणियाँ
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 3d76afe65..e223a40a0 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -148,7 +148,7 @@
Zaštita
Baza podataka sadrži duplicirane UUID-ove.
Modus odabira
- Algoritam šifriranja baze podataka koji se koristi za sve podatke.
+ Algoritam šifriranja baze podataka koji se koristi za sve podatke
Korištenje memorije
Paralelnost
Stupanj paralelnosti (odnosno broj threadova) koji će koristiti funkcija za generiranje ključeva.
@@ -599,7 +599,7 @@
Ostavi ekran uključen
Ostavi ekran uključen tijekom gledanja unosa
Boje unosa
- Prikazuje prednje boje i boje pozadine u unosu
+ Prikazuje prednje boje i boje pozadine za unos
Prednja boja unosa
Boja baze podataka
Pozadinska boja unosa
@@ -610,7 +610,7 @@
Pretraživo
Naslijedi
Prilagođeni podaci
- Slijed automatskog popunjavanja
+ Slijed automatskog unosa
Regularni izraz
Sjedini iz …
Spremi kopiju u …
@@ -639,4 +639,28 @@
Velika Početna Slova
Broj znakova: %1$d
VELIKA SLOVA
+ Hardverski ključ
+ Odaberi hardverski ključ.
+ Spajanje iz baze podataka V1 nije moguće.
+ Mjesto baze podataka nije poznato, radnja baze podataka se ne može izvršiti.
+ Korisnik je prekinuo radnju.
+ Potreban je pogon za %1$s.
+ Hardverski ključ nije podržan.
+ Ključ ne smije biti prazan.
+ Neispravna datoteka.
+ Pamti korištene hardverske ključeve
+ Modus snimanja ekrana
+ Neispravno formatiran XML.
+ Modus snimanja ekrana
+ Potvrdni okvir hardverskog ključa
+ Zapamti hardverske ključeve
+ Dopusti stranim aplikacijama snimanje ili slikanje ekrana aplikacije
+ <strong>Korisnički podaci se ne dohvaćaju</strong>, ova aplikacija se ne povezuje ni s jednim poslužiteljem, radi samo lokalno i u potpunosti poštuje privatnost korisnika.
+ Nije moguće dobiti odgovor na izazov.
+ Zaglavlje navigacije
+ Čeka se odgovor za izazov …
+ Automatski unos
+ Čeka se zahtjev za izazov …
+ Izazov je već zatražen
+ Odgovor je već dat.
\ No newline at end of file
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index c0e8be6a5..6be45ca89 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -35,8 +35,8 @@
Secara otomatis tutupi kata sandi (***)
Sembunyikan Kata Sandi
Huruf Kecil
- Memuat basisdata…
- Pembuatan basisdata…
+ Memuat basis data…
+ Pembuatan basis data…
Ukuran teks dalam daftar elemen
Ukuran daftar item
Menampilkan jumlah entri dalam sebuah grup
@@ -45,7 +45,7 @@
Tampilkan nama pengguna
Panjangnya
File Kunci kosong.
- Tidak bisa mengenali format basisdata.
+ Tidak bisa mengenali format basis data.
%1$s dengan UUID yang sama %2$s sudah ada.
Algoritma salah.
Tidak bisa membaca kredensial.
@@ -58,7 +58,7 @@
Konfirmasi Kata Sandi
Buatkan Kata Sandi
Pengelola File
- Tidak bisa menemukan file. Cobalah buka kembali dari pengelola file anda.
+ Tidak bisa menemukan file. Cobalah buka kembali dari pengelola file Anda.
Nilai Bidang
Nama Bidang
Teks ini tidak sesuai dengan item yang diminta.
@@ -66,8 +66,8 @@
Periode harus antara %1$d dan %2$d detik.
Penghitung harus antara %1$d dan %2$d.
Kunci rahasia harus dalam format Base32.
- Tidak bisa menyimpan basisdata.
- Tidak bisa membuat basisdata dengan kata sandi dan file kunci ini.
+ Tidak dapat menyimpan basis data.
+ Tidak bisa membuat basis data dengan kata sandi dan file kunci ini.
Tidak bisa membuat berkas database.
Anda tidak bisa menyalin grup di sini.
Anda tidak dapat menyalin entri di sini.
@@ -81,18 +81,18 @@
Setidaknya ada satu kredensial yang harus ditetapkan.
Setidaknya ada satu jenis pembuatan kata sandi yang harus dipilih.
Tidak bisa memuat kunci. Cobalah untuk mengurangi penggunaan memori (mematikan aplikasi lainnya).
- Tidak bisa memuat basisdata anda.
- Tidak cukup memori untuk memuat seluruh basisdata anda.
+ Tidak dapat memuat basis data.
+ Tidak cukup memori untuk memuat seluruh basis data Anda.
File Kunci
Pilih file kunci.
Ketik sebuah nama.
Rahasia OTP tidak valid.
Pastikan lokasi filenya sudah benar.
- Tidak bisa membaca basisdata.
+ Tidak bisa membaca basis data.
Tidak bisa membuat file
Tambahkan Lampiran
Dijit
- Waktu idle sebelum mengunci basisdata
+ Waktu idle sebelum mengunci basis data
Manajer file yang menerima ACTION_CREATE_DOCUMENT ACTION_OPEN_DOCUMENT izin diperlukan untuk membuat, membuka, dan menyimpan file database.
Jenis OTP
Penyiapan Sandi Sekali Pakai (OTP)
@@ -110,9 +110,9 @@
Simpan
Kata Sandi
Tidak bisa menemukan data entri.
- Mengambil kunci basisdata…
+ Mengambil kunci basis data…
Tidak bisa membersihkan papan klip
- Durasi simpan pada papan klip (jika didukung oleh perangkat anda)
+ Durasi simpan pada papan klip (jika didukung oleh perangkat Anda)
Ulangi Peralihan Penampakan Kata Sandi
Batas Waktu Papan Klip
Papan Klip Dibersihkan
@@ -129,8 +129,8 @@
Untuk <strong>menjaga kebebasan kami</strong>, <strong>memperbaiki bug</strong>, <strong> menambah fitur</strong> dan <strong>agar selalu aktif</strong>, kami mengandalkan <strong> kontribusi</strong>.
KeePassDX © %1$d Kunzisoft <strong>open source</strong> dan <strong>tanpa iklan</strong>.
\nTersedia apa adanya, di bawah lisensi <strong>GPLv3</strong>, tanpa jaminan apa pun.
- Gunakan sebagai basisdata standar
- Mendekripsi konten basisdata…
+ Gunakan sebagai basis data standar
+ Mendekripsi konten basis data…
BasisData
Pilih untuk menyalin %1$s ke papan klip
Tutup Bidang
@@ -177,21 +177,21 @@
Kontak
Minta pencarian saat membuka database
Pencarian cepat
- Buat basisdata baru
- Buka basisdata yang sudah ada
+ Buat basis data baru
+ Buka basis data yang sudah ada
Pasang browser web untuk membuka URL ini.
Penggunaan memori
Putaran transformasi
- Algoritma enkripsi basisdata digunakan untuk semua data.
+ Algoritma enkripsi basis data digunakan untuk semua data
Root
- Sembunyikan tautan yang rusak dalam daftar basisdata terbaru
- Sembunyikan tautan basisdata yang rusak
- Tampilkan lokasi dari basisdata terbaru
+ Sembunyikan tautan yang rusak dalam daftar basis data terbaru
+ Sembunyikan tautan basis data yang rusak
+ Tampilkan lokasi dari basis data terbaru
Tampilkan file sebelumnya
Melacak di mana file kunci disimpan
Ingat lokasi file kunci
Melacak di mana database disimpan
- Ingat lokasi basisdata
+ Ingat lokasi basis data
Mode seleksi
Selesaikan masalah dengan menghasilkan UUID baru, lanjutkan\?
Basisdata berisi UUIDs duplikat.
@@ -199,7 +199,7 @@
Terlindung dari penulisan
Perlindungan
Sedang bekerja…
- Membuat basisdata baru…
+ Membuat basis data baru…
Cari Domain Web dengan Kendala Subdomain
Pencarian Subdomain
Jumlah maksimal
@@ -227,7 +227,7 @@
Judul
Jangan tutup aplikasi…
Menjalankan perintah…
- Menyimpan basisdata…
+ Menyimpan basis data…
Tidak dapat membangun ulang daftar.
Magikeyboard
Papan tik
@@ -377,29 +377,29 @@
Timbul galat saat mengunggah data berkas.
File yang Anda unggah terlalu besar.
Info sandi satu kali
- Buka basisdata yang sudah ada
- Simpan otomatis basisdata
- Tambah item ke basisdata anda
+ Buka basis data yang sudah ada
+ Simpan otomatis basis data
+ Tambah item ke basis data Anda
Sunting entri
- Buat berkas basisdata anda
+ Buat berkas basis data Anda
Hapus kata sandi
Pengurutan item
Terima kasih banyak atas kontribusinya.
- Dengan berkontribusi,
- Kunci basisdata
- Buka basisdata anda
+ Dengan <strong>berkontribusi</strong>,
+ Kunci basis data
+ Buka basis data Anda
Salin bidang
Atur OTP
Partisipasi
- Buka basisdata dengan pengenalan pembuka kunci tingkat lanjut
+ Buka basis data dengan pengenalan pembuka kunci tingkat lanjut
Anda tetap harus mengingat kata sandi utama Anda jika Anda menggunakan pengenalan pembukaan kunci tingkat lanjut.
- Akses ke berkas dicabut oleh pengelola berkas, tutup basisdata dan buka kembali dari lokasi tempatnya.
+ Akses ke berkas dicabut oleh pengelola berkas, tutup basis data dan buka kembali dari lokasi tempatnya.
Gabungkan data, timpa perubahan dengan cara menyimpan database atau muat ulang dengan perubahan terbaru.
- Informasi yang tersimpan di berkas basisdata Anda sudah diubah di luar aplikasi.
+ Informasi yang tersimpan di berkas basis data Anda sudah diubah di luar aplikasi.
Lanjut tanpa kunci enkripsi\?
Lanjut tanpa proteksi buka kunci dengan kata sandi\?
Akses ke berkas dicabut oleh pengelola berkas
- Berikan akses tulis berkas untuk menyimpan perubahan basisdata
+ Berikan akses tulis berkas untuk menyimpan perubahan basis data
Setelan
Galat ketika mengekspor setelan aplikasi
Setelan aplikasi diekspor
@@ -410,7 +410,7 @@
Ekspor setelan aplikasi
Pilih sebuah berkas untuk mengimpor setelan aplikasi
Impor setelan aplikasi
- Timbul galat ketika melaksanakan sebuah aksi di basisdata.
+ Timbul galat ketika melaksanakan sebuah aksi di basis data.
Anda tidak dapat memindahkan sebuah grup ke sini.
Membuka kredensial perangkat
Memperbolehkan Anda memindai biometrik Anda untuk membuka basis data
@@ -488,7 +488,7 @@
\n\"Terlindungi-tulis\" mencegah perubahan tak diinginkan ke basis data.
\n\"Bisa dimodifikasi\" memungkinkan Anda untuk menambah, menghapus, atau menyunting semua elemen yang Anda inginkan.
Tampilkan token OTP dalam daftar entri
- Dengan membeli versi pro, Anda akan mendapatkan akses ke gaya visual ini dan Anda akan terutama membantu realisasi proyek komunitas.
+ Dengan membeli versi pro, Anda akan mendapatkan akses ke <strong>gaya visual </strong> ini dan Anda akan terutama membantu <strong>realisasi proyek komunitas.</strong>
Kunci basis data ketika durasi papan klip kedaluwarsa atau notifikasi ditutup setelah Anda mulai menggunakannya
Peringatan: Papan klip dipakai oleh semua aplikasi. Jika data sensitif disalin, perangkat lunak lain mungkin dapat mendapatkannya.
Kunci basis data Anda dengan cepat, Anda bisa mengatur aplikasi untuk mengunci sendiri setelah beberapa saat dan ketika layar berubah mati.
@@ -514,17 +514,17 @@
\n
\nGunakan metode pengisian formulir yang Anda sukai.
Bantu meningkatkan stabilitas, keamanan, dan menambahkan lebih banyak fitur.
- Tidak seperti banyak aplikasi pengelola kata sandi, ini bebas iklan, perangkat lunak libre copyleft, dan tidak mengumpulkan data pribadi di peladennya, tidak peduli versi apa yang Anda gunakan.
- Gaya visual ini tersedia berkat kemurahan hati Anda.
- Untuk menjaga kebebasan kami dan untuk kami selalu aktif, kami mengandalkan kontribusi Anda.
- Fitur ini sedang dalam pengembangan dan memerlukan kontribusi Anda untuk bisa tersedia segera.
+ Tidak seperti banyak aplikasi pengelola kata sandi, ini <strong>bebas iklan</strong>, <strong>perangkat lunak libre copyleft</strong>, dan tidak mengumpulkan data pribadi di peladennya, tidak peduli versi apa yang Anda gunakan.
+ <strong>Gaya visual</strong> ini tersedia berkat kemurahan hati Anda.
+ Untuk menjaga kebebasan kami dan untuk kami selalu aktif, kami mengandalkan <strong>kontribusi</strong> Anda.
+ Fitur ini <strong>sedang dalam pengembangan</strong> dan memerlukan <strong>kontribusi</strong> Anda untuk bisa tersedia segera.
Tampilkan Token OTP
Teks
Warna basis data kustom
Pembaruan paksa lain kali
Pembaruan paksa
- Anda menyemangati pengembang untuk membuat fitur baru dan untuk memperbaiki kutu sesuai dengan komentar Anda.
- Dengan membeli versi pro,
+ Anda menyemangati pengembang untuk membuat <strong>fitur baru</strong> dan untuk <strong>memperbaiki kutu</strong> sesuai dengan komentar Anda.
+ Dengan membeli versi <strong>pro</strong>,
Warna basis data
Gabung data
Memuat ulang database akan menghapus data yang diubah secara lokal.
@@ -594,7 +594,7 @@
Petunjuk pendidikan
Buka file basis data Anda sebelumnya dari peramban file Anda untuk terus menggunakannya.
Warna entri
- Menampilkan warna latar depan dan latar belakang dalam entri
+ Menampilkan warna latar depan dan latar belakang untuk sebuah entri
Atur ulang petunjuk pendidikan
Tampilkan semua info pendidikan lagi
Edit entri Anda dengan bidang kustom. Kumpulan data dapat dirujuk antara bidang entri yang berbeda.
@@ -622,4 +622,28 @@
Coba simpan informasi terbagi ketika membuat sebuah pilihan entri manual untuk penggunaan mudah di waktu mendatang
Terlindungi-tulis
Jaga layar tetap menyala saat melihat entri
+ Kotak centang kunci perangkat keras
+ Menunggu untuk permintaan tantangan…
+ Menunggu untuk tanggapan tantangan…
+ Kunci perangkat keras
+ Pilih sebuah kunci perangkat keras.
+ XML tidak benar.
+ Tantangan sudah diminta
+ Tanggapan sudah disediakan.
+ Tidak bisa mendapatkan tanggapan dari tantangan.
+ Dibatalkan oleh pengguna.
+ Driver untuk %1$s dibutuhkan.
+ Tidak dapat menggabungkan dari sebuah basis data V1.
+ Lokasi basis data tidak diketahui, tindakan basis data tidak dapat dilakukan.
+ Kunci perangkat keras tidak didukung.
+ Kunci tidak bisa kosong.
+ Berkas terkorup.
+ Ingat kunci perangkat keras
+ Mode tangkapan layar
+ Mode tangkapan layar
+ Mengingat kunci perangkat keras yang telah digunakan
+ Perbolehkan aplikasi pihak ketiga untuk merekam atau mengambil tangkapan layar aplikasi
+ <strong>Tidak ada data pengguna yang didapatkan</strong>, aplikasi ini tidak menghubungkan ke server apa pun, bekerja secara lokal dan menghargai privasi pengguna.
+ Tajuk navigasi
+ Ketik Otomatis
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 8829e85f2..77d36a22f 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -157,7 +157,7 @@
Annulla
Sola lettura
Modificabile
- Algoritmo di cifratura del database usato per tutti i dati.
+ Algoritmo di cifratura del database usato per tutti i dati
Per generare la chiave per l\'algoritmo di cifratura, la chiave principale viene trasformata usando una funzione di derivazione della chiave (con un sale casuale).
Utilizzo di memoria
Quantità di memoria utilizzabili dalla funzione di derivazione della chiave.
@@ -370,7 +370,7 @@
Ricerca rapida
Cancella cronologia
Ripristina cronologia
- Per poter mantenere la nostra libertà, correggere errori, aggiungere funzionalità ed essere sempre attivi, facciamo affidamento sul tuo contributo.
+ Per poter <strong>mantenere la nostra libertà</strong>, <strong>correggere errori</strong>, <strong>aggiungere funzionalità</strong> ed <strong>essere sempre attivi</strong>, facciamo affidamento sul tuo <strong>contributo</strong>.
Contatto
Il keystore non è inizializzato correttamente.
Impostazioni della chiave principale
@@ -610,7 +610,7 @@
Tieni lo schermo acceso
Tieni lo schermo acceso mentre si vede la voce
Colori della voce
- Mostra colori in evidenza e in secondo piano in una voce
+ Mostra colori in evidenza e in secondo piano per una voce
Dati personalizzati
Gruppo attuale
Apertura del cassetto di navigazione
@@ -628,10 +628,11 @@
Frase di accesso
Colora le password
Colora i tipi di carattere della password
- Un database è già aperto, chiuderlo prima di aprirne un altro.
+ Un database è già aperto, chiuderlo prima di aprirne un altro
Sequenza di autodigitazione
Questa funzione memorizzerà le informazioni cifrate di accesso nel KeyStore protetto del dispositivo.
-\nA seconda di come è stata implementata l\'API nativa del sistema operativo, potrebbe non essere pienanente funzionante.
+\n
+\nA seconda di come è stata implementata l\'API nativa del sistema operativo, potrebbe non essere pienamente funzionante.
\nVerificare la compatibilità e la sicurezza del KeyStore consultando sia il costruttore del dispositivo che il creatore della ROM in uso nel dispositivo stesso.
Schermata di ricerca
Ritornare automaticamente alla tastiera precedente quando ci si trova sulla schermata di ricerca
@@ -647,4 +648,27 @@
Iniziali maiuscole
Numero di caratteri: %1$d
Entropia: %1$s bit
+ File danneggiato.
+ Consenti alle app di terze parti di registrare o acquisire schermate dell\'app
+ Annullato dall\'utente.
+ Impossibile eseguire l\'unione da un database V1.
+ Il percorso del database è sconosciuto, non è possibile eseguire l\'azione sul database.
+ Ricorda le chiavi hardware
+ Tiene traccia delle chiavi hardware usate
+ Checkbox chiave hardware
+ In attesa della richiesta di verifica…
+ In attesa della risposta alla verifica…
+ Chiave hardware
+ Seleziona una chiave hardware.
+ XML malformato.
+ Verifica già richiesta
+ Risposta già fornita.
+ Impossibile ottenere una risposta dalla verifica.
+ È necessario un driver per %1$s.
+ La chiave hardware non è supportata.
+ La chiave non può essere vuota.
+ Modalità di cattura schermo
+ Modalità di cattura schermo
+ <strong>Nessun dato dell\'utente viene prelevato</strong>, questa applicazione non si connette ad alcun server, funziona solo in locale e rispetta pienamente la privacy degli utenti.
+ Auto-digita
\ No newline at end of file
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 291fda3db..3baa29ccd 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -20,30 +20,30 @@
-->
משוב
דף הבית
- KeePassDX היא תוכנה המממשת את מנהל הסיסמאות KeePass לאנרואיד.
+ מימוש אנדרואיד של מנהל הסיסמאות KeePass
קבל
הוסף ערך
הוסף קבוצה
- אלגוריתם
- פסק זמן ליישום
+ אלגוריתם הצפנה
+ פסק זמן
זמן לפני נעילת מסד הנתונים כאשר היישום לא פעיל
יישום
הגדרות יישום
סוגריים
- סייר הקבצים דורש את סייר הקבצים Open Intents, לחץ למטע כדי להתקין. בגלל מספר בעיות בסייר, ייתכן ויהיו בעיות בהפעלה הראשונה.
- לוח ההעתקה נוקה.
- שגיאת לוח ההעתקה
- במספר מכשירי אנרואיד מסמסונג קיים באג במימוש לוח ההעתקה שיכול לגרום לבעיות בהעתקה מהיישום. לעוד מידע עבור אל:
- ניקוי לוח ההעתקה נכשל
+ נדרש סייר קבצים המקבל intent actions של ACTION_CREATE_DOCUMENT ו ACTION_OPEN_DOCUMENT בכדי ליצור, לפתוח, ולשמור קבצי מסד נתונים.
+ לוח ההעתקה נוקה
+ שגיאת לוח העתקה
+ מכשירים מסויימים לא יאפשרו ליישומים להשתמש בלוח ההעתקה.
+ לא ניתן לנקות את לוח ההעתקה
זמן עד לניקוי לוח ההעתקה
- זמן לפני שלוח ההעתקה מנוקה לאחר העתקת שם משתמש או סיסמה
- בחר בהעתקת %1$s ללוח ההעתקה
- יוצר מפתח מסד נתונים…
+ זמן אחסון בלוח ההעתקה (אם נתמך על ידי מכשירך)
+ בחר בכדי להעתיק %1$s ללוח ההעתקה
+ מאחזר מפתח מסד נתונים…
מסד נתונים
מפענח תוכן מסד נתונים…
- השתמש בזה כמסד הנתונים הברירת מחדל
+ השתמש כמסד נתונים ברירת מחדל
ספרות
- הזן שם קובץ למסד נתונים:
+ פתח מסד נתונים קיים
ניגש לאחרונה
בטל
הערות
@@ -52,7 +52,7 @@
פג תוקף
קובץ מפתח
נערך לאחרונה
- נתוני ערך לא נמצאו.
+ לא ניתן למצוא נתוני ערך.
סיסמה
שמור
שם
@@ -60,16 +60,16 @@
צופן זרם Arcfour אינו נתמך.
KeePassDX לא יכול לטפל ב-URI הזה.
לא הצליח ליצור קובץ
- מסד נתונים לא חוקי.
- נתיב לא חוקי.
- שם נדרש.
- סיסמה או קובץ מפתח נדרשים.
- זיכרון המכשיר אזל בזמן ניתוח מסד הנתונים. יתכן והוא גדול מדי למכשירך.
- לפחות סוג אחד ליצירת סיסמה צריך להיבחר
+ לא ניתן לקרוא את מסד הנתונים.
+ ודא שהנתיב נכון.
+ הכנס שם.
+ בחר קובץ מפתח.
+ אין זיכרון לטעינת כל מסד הנתונים שלך.
+ יש לבחור לפחות סוג יצירת סיסמה אחד.
הסיסמאות לא תואמות.
- מספר סיבובים גדול מדי. מגדיר ל-2147483648.
- שדה שם נדרש לכל מחרוזת.
- הזן מספר חיובי בשדה האורך
+ \"סבבי שינוי\" גדולים מדי. מגדיר ל-2147483648.
+ לכל מחרוזת צריך להיות שם שדה.
+ הזן מספר שלם חיובי בשדה \"אורך\".
שם השדה
ערך השדה
סייר קבצים
@@ -77,14 +77,14 @@
אשר סיסמה
סיסמה שנוצרה
שם קבוצה
- קובץ המפתח
+ קובץ מפתח
אורך
סיסמה
סיסמה
- סיסמה או קובץ מפתח לא מתאימים.
- אלגוריתם לא חוקי.
- תבנית מסד הנתונים אינה מזוהה.
- קובץ מפתח ריק.
+ לא ניתן לקרוא אישורים.
+ אלגוריתם שגוי.
+ לא ניתן לזהות את פורמט מסד הנתונים.
+ קובץ המפתח ריק.
אורך
גודל רשימה קבוצות
גודל הטקסט ברשימת הקבוצות
@@ -108,7 +108,7 @@
מינוס
אף פעם
אין תוצאות חיפוש
- אין מטפל לכתובת url זו.
+ התקן דפדפן אינטרנט כדי לפתוח כתובת אתר זו.
צור מסד נתונים חדש…
עובד…
הגנה
@@ -130,9 +130,188 @@
- בינוני
- גדול
- ערוך רשומה
+ ערוך ערך
הצפנה
אפשר
כתובת URL
- לא ניתן לטעון את בסיס הנתונים
+ לא ניתן לטעון את מסד הנתונים.
+ פג תוקף
+ מספר מילים של ביטוי סיסמה
+ סגור שדות
+ הקלדה אוטומטית
+ Wi-Fi
+ ביטוי סיסמה
+ תגים
+ אלגוריתם
+ כרטיס חיוב / אשראי
+ CVV
+ קוד סודי
+ סוג
+ ארנק מטבעות קריפטו
+ Token
+ מפתח ציבורי
+ מפתח פרטי
+ זרע
+ IBAN
+ הערה מאובטחת
+ תֶקֶן
+ חברות
+ ה-Token חייב להכיל %1$d עד %2$d ספרות.
+ צבע מסד הנתונים
+ צבע קדמה של הערך
+ מזהה אוניברסלי ייחודי
+ היסטוריה
+ קבצים מצורפים
+ מחזיק
+ מספר
+ דוא\"ל
+ כתובת דואר אלקטרוני
+ מילה זו שמורה ולא ניתן להשתמש בה.
+ תווית זו כבר קיימת.
+ אירעה שגיאה בעת הסרת נתוני הקובץ.
+ הקובץ שהינך מנסה להעלות גדול מידי.
+ פרטי סיסמה חד-פעמית
+ סוג הסיסמה החד-פעמית הקיים אינו מזוהה בטופס זה, ייתכן שהאימות שלו לא יחולל יותר כראוי את ה-Token.
+ שם הסמל
+ <strong>אין אחזור של נתוני משתמש</strong>, יישום זה אינו מתחבר לשום שרת, פועל באופן מקומי בלבד ומכבד באופן מלא את פרטיות המשתמשים.
+ מפתח פיזי
+ נתונים מותאמים אישית
+ סוג סיסמה חד-פעמית
+ סוד הסיסמה החד-פעמית
+ SSID
+ חשבון
+ תבנית
+ טווח זמן (שניות)
+ מונה
+ גירסה
+ סיסמה חד-פעמית
+ לא ניתן לטעון את המפתח. נסה להוריד את ה\"שימוש בזיכרון\" של KDF.
+ לא ניתן להפעיל את שירות המילוי האוטומטי.
+ אינך יכול להעתיק ערך כאן.
+ המונה חייב להיות בין %1$d ל-%2$d.
+ טווח הזמן חייב להיות בין %1$d ל-%2$d שניות.
+ שמירה של פריט חדש אינה מותרת במסד נתונים עם קריאה בלבד
+ טקסט זה אינו תואם את הפריט המבוקש.
+ שם השדה כבר קיים.
+ לא ניתן לאחזר URI של מסד הנתונים.
+ לא ניתן לבנות מחדש את הרשימה כראוי.
+ אירעה שגיאה בעת העלאת נתוני הקובץ.
+ אירעה שגיאה בעת ביצוע פעולה על מסד הנתונים.
+ האתגר כבר התבקש
+ תגובה כבר ניתנה.
+ לא ניתן לקבל את התגובה מהאתגר.
+ בוטל על ידי המשתמש.
+ נדרש מנהל התקן עבור %1$s.
+ מיקום מסד הנתונים אינו ידוע, לא ניתן לבצע פעולת מסד נתונים.
+ לא ניתן למזג ממסד נתונים V1.
+ מפתח פיזי אינו נתמך.
+ המפתח לא יכול להיות ריק.
+ לא ניתן למצוא את הקובץ. נסה לפתוח אותו מחדש מסייר הקבצים שלך.
+ קובץ פגום.
+ תבניות
+ הגדר סיסמה חד-פעמית
+ יש להגדיר לפחות אישור אחד.
+ אינך יכול להעביר ערך כאן.
+ בטל
+ סמל הערך
+ תיבת סימון קובץ מפתח
+ תיבת סימון סיסמה
+ פרטי האישורים
+ פרטי הקובץ
+ הוסף פריט
+ הוסף קבוצה
+ הוסף ערך
+ הוסף חוליה
+ פתח קובץ
+ ילדי החוליה
+ רקע
+ איש קשר
+ תרומה
+ מפתח ראשי
+ פונקציית גזירת מפתח
+ כותרת ניווט
+ פתיחת מגירת ניווט
+ סגירת מגירת ניווט
+ KeePassDX © %1$d Kunzisoft הוא יישום <strong>קוד פתוח</strong> ו<strong>בלי פרסום</strong>.
+\nהוא ניתן כפי שהוא, תחת רישיון <strong>GPLv3</strong>, ללא כל אחריות.
+ בכדי <strong>לשמור על החופש שלנו</strong>, <strong>לתקן באגים</strong>, <strong>להוסיף תכונות</strong> <strong>ולהיות תמיד פעילים</strong>, אנו סומכים על <strong>תרומתך</strong>.
+ ניתן לחיפוש
+ לָרֶשֶׁת
+ רצף הקלדה אוטומטית
+ מסנני חיפוש
+ רגיש לאותיות גדולות/קטנות
+ ביטוי רגולרי
+ תעודת זהות
+ מקום הנפקה
+ תאריך הנפקה
+ ספרות
+ קבוצה נוכחית
+ שם
+ בנק
+ שם הבנק
+ SWIFT / BIC
+ לא ניתן לשמור את מסד הנתונים.
+ המפתח הסודי חייב להיות בפורמט Base32.
+ אינך יכול להעביר קבוצה כאן.
+ אינך יכול להעתיק קבוצה כאן.
+ לא ניתן ליצור קובץ מסד נתונים.
+ לא ניתן ליצור מסד נתונים עם סיסמה וקובץ מפתח אלה.
+ נתוני הקובץ כבר קיימים.
+ צבע רקע של הערך
+ אימות
+ צרף קובץ
+ הסר שדה
+ עדכן
+ ממתין לבקשת האתגר…
+ ממתין לתגובת האתגר…
+ בחר מפתח פיזי.
+ XML פגום.
+ סוד הסיסמה החד-פעמית לא חוקי.
+ לבטל שינויים\?
+ אבטחה
+ ASCII מורחב
+ מחולל סיסמאות
+ אורך סיסמה
+ תיבת סימון מפתח אבטחה
+ החלפת נראות הסיסמה
+ הוסף שדה
+ מילוי טפסים
+ ביטול נעילה מתקדם
+ שמור נתונים
+ מזג נתונים
+ הצג מספר ערכים
+ טען מחדש נתונים
+ הצג UUID
+ מציג OTP tokens ברשימת הערכים
+ מציג את מספר הערכים בקבוצה
+ סמל חיצוני
+ חיפוש מהיר
+ בקש חיפוש בעת פתיחת מסד נתונים
+ צבע סיסמאות
+ צבע תווי סיסמה לפי סוג
+ מציג שמות משתמשים ברשימות ערכים
+ הצג OTP token
+ מציג את ה-UUID המקושר לערך או לקבוצה
+ יוצר מסד נתונים…
+ עותק של %1$s
+ שמור עותק ב …
+ מזג מ …
+ מוגן כתיבה
+ ניתן לשינוי
+ רוקן את סל המחזור
+ שחזר היסטוריה
+ מחק היסטוריה
+ חפש דומיינים של אינטרנט עם הגבלות תת-דומיינים
+ מוגן כתיבה
+ הגדרות מפתח ראשי
+ הצג שמות משתמש
+ הגדרות אבטחה
+ העבר
+ הדבק
+ בטל
+ העתק
+ צור מסד נתונים חדש
+ חיפוש תת-דומיין
+ מחק מפתח ביטול נעילה מתקדם
+ %1$s עם אותו UUID %2$s כבר קיים.
\ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 0bc43839b..562213414 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -411,7 +411,7 @@
教育的なヒントをリセットしました
データベース ファイルを作成
最初のパスワード管理ファイルを作成します。
- 既存のデータペースを開く
+ 既存のデータベースを開く
これまで使ってきたデータベース ファイルを、ファイルブラウザから開いて引き続き使用します。
データベースに項目を追加
エントリーはデジタル ID の管理に役立ちます。\n\nグループ(≒フォルダ)はデータベース内のエントリーを整理します。
@@ -443,7 +443,7 @@
参加
安定性やセキュリティを向上させ、機能を追加することを支援します。
多くのパスワード管理アプリとは異なり、このアプリは<strong>広告なし</strong>かつ<strong>コピーレフトの自由ソフトウェア</strong>です。どのバージョンを使っても、サーバー上で個人情報が収集されることはありません。
- Pro バージョンを購入すると、このビジュアル スタイルにアクセスできるようになり、またコミュニティ プロジェクトの実現を特に支援できます。
+ Pro バージョンを購入すると、この<strong>ビジュアル スタイル</strong>にアクセスできるようになり、また<strong>コミュニティ プロジェクトの実現</strong>を特に支援できます。
この<strong>ビジュアル スタイル</strong>はあなたの厚意により利用可能となります。
自由を維持し活発に開発し続けるために、私たちはあなたの<strong>貢献</strong>に期待しています。
この機能は<strong>開発中</strong>であり、早期に提供するにはあなたの<strong>貢献</strong>が必要です。
@@ -633,7 +633,7 @@
\nKeyStore の互換性と安全性については、お使いのデバイスの製造元および使用するROMの作成者にご確認ください。
ナビゲーション ドロワーが開いています
ナビゲーション ドロワーが閉じています
- Auto-Type シークエンス
+ 自動入力シークエンス
選択されたグループ
検索画面で切り替え前のキーボードに自動的に戻します
マージ…
@@ -645,4 +645,22 @@
対象に加える文字
使用しない文字
語頭を大文字
+ サードパーティのアプリでこのアプリのスクリーンショットを撮れるようにします
+ スクリーンショット モード
+ <strong>ユーザーからデータを取得しません</strong>。このアプリケーションは特定のサーバーに接続せず、ローカルでのみ動作し、ユーザーのプライバシーを完全に尊重します。
+ ハードウェアキー
+ ハードウェアキーを選択してください。
+ XMLの形式が正しくありません。
+ ハードウェアキーには対応していません。
+ ハードウェアキーを記憶
+ ハードウェアキーの使用記録を保持します
+ スクリーンショット モード
+ ナビゲーション ヘッダー
+ ハードウェアキーのチェックボックス
+ キーを空にすることはできません。
+ ファイルが破損しています。
+ ユーザーによってキャンセルされました。
+ データベースの場所が不明です。データベースへの操作を実行できません。
+ %1$s 用のドライバーが必要です。
+ 自動入力
\ No newline at end of file
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 6a5d4aef0..a032d8436 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -46,7 +46,8 @@
데이터베이스 컨텐츠 암호 해독 중…
기본 데이터베이스로 사용
단위
- KeePassDX © %1$d Kunzisoft는 보증이 적용되지 않습니다; 이것은 자유 소프트웨어이며, GPL 버전 3 또는 그 이상의 조건으로의 재배포를 환영합니다.
+ KeePassDX © %1$d Kunzisoft는 보증이 적용되지 않습니다; 이것은 자유 소프트웨어이며, 광고가 없습니다.
+\n이 것은 보증 없이 있는 그대로, GPL 버전 3하에 제공됩니다.
접근됨
취소
노트
@@ -125,7 +126,7 @@
쓰기 보호됨
수정 가능
마이너스
- 정대
+ 절대 하지 않음
검색 결과가 없음
이 URL을 열기 위해 웹 브라우저를 설치하십시오.
가지고 있는 데이터베이스 열기
@@ -192,11 +193,160 @@
문의
갱신
검증
- 우리의 자유를 지키고, 버그를 고치고, 기능을 추가하고, 언제나 활성화되기 위해 우리는 당신의 기여를 믿습니다.
+ <strong>우리의 자유를 지키고</strong>, <strong>버그를 고치고</strong>, <strong>기능을 추가</strong>하고, <strong>언제나 활성화</strong>되기 위해 우리는 당신의 <strong>기여</strong>를 믿습니다.
일회용 비밀번호 설정
OTP 유형
주기 (초)
검색 필터
정규 표현식
이미 존재하는 라벨입니다.
+ 백그라운드
+ 커스텀 데이터
+ 이름
+ 맴버쉽
+ 표준
+ 템플렛
+ OTP
+ 새로운 아이템을 저장하는 것은 읽기 전용 데이터베이스에서 허용되지 않습니다
+ 데이터베이스 URI를 검색할 수 없습니다.
+ UUID
+ 홀더
+ 토큰
+ 공개 키
+ 시드
+ SWIFT 또는 BIC
+ 구간은 반드시 %1$d과 %2$d사이여야 합니다.
+ 템플렛들
+ 필드 닫기
+ 기록
+ 첨부 파일들
+ 자동 입력 스퀀스
+ 스크릿
+ 카운터
+ 숫자들
+ CVV
+ 이메일
+ 이메일 주소
+ SSID
+ 개인 키
+ 계정
+ 은행
+ 하드웨어 키 체크박스
+ 버리기
+ 인증 요청을 기다리는 중…
+ 인증 응답을 기다리는 중…
+ 하드웨어 키
+ 태그들
+ 현재 그룹
+ 숫자
+ PIN
+ Wi-Fi
+ IBAN
+ 안전한 노트
+ 버전
+ 하드웨어 키를 선택하세요.
+ 카운터는 반드시 %1$d과 %2$d사이여야 합니다.
+ 그 텍스트는 요청된 아이템과 일치하지 않습니다.
+ 그 필드 이름은 이미 존재합니다.
+ 상속
+ 자동입력
+ 데이터베이스가 이미 열려있습니다. 새 것을 열려면 지금 것을 먼저 닫아주세요.
+ 최근 데이터베이스 목록에서 파손된 링크를 감춤
+ 앱 속성을 내보낼 파일을 선택
+ 앱 속성을 가져왔습니다.
+ 암호화 알고리즘 용 키를 생성하기 위해, 마스터키는 임의의 솔트(salt) 키 파생 함수를 사용하여 변환됩니다.
+ 경고
+ 이미 요청된 시도입니다
+ XML 양식이 틀어짐.
+ 파일 데이터를 업로드하는 중 오류가 발생했습니다.
+ 이미 응답했습니다.
+ 데이터 베이스 위치를 알 수 없어 데이터 베이스 액션을 수행할 수 없습니다.
+ %1$s 를 위한 드라이버가 요구됩니다.
+ 데이터베이스 V1로부터 병합할 수 없습니다.
+ 하드웨어 키는 지원하지 않습니다.
+ 키는 반드시 입력해야 합니다.
+ 아이콘명
+ 암호문
+ 비밀번호에 색상 부여
+ 타입에 따라 비밀번호 문자에 색상을 부여
+ 앱 속성을 내보내기
+ 대문자
+ 보안 설정
+ 데이터 다시 읽기
+ 휴지통 비우기
+ 데이터베이스를 열 때 검색을 요청
+ 빠른 검색
+ 키 파일 저장된 곳을 추적
+ 저장 모드
+ 선택 모드
+ 하드웨어 키를 기억
+ 앱 속성을 내보낼 파일을 생성
+ 앱 속성을 가져오던 중 오류 발생
+ 데이터베이스가 중복된 UUID를 포함하고 있습니다.
+ 데이터 베이스가 저장된 곳을 추적
+ 키 파일 위치를 기억
+ 하드웨어 키 사용을 추적
+ 기존 OTP 종류가 본 양식에서 인식되지 않습니다. 그 유효성은 더이상 토큰값 생성을 정확히 못할 수 있습니다.
+ 보조 도메인 검색
+ 데이터베이스 위치를 기억
+ 최근 파일을 표시
+ 최근 데이터베이스의 위치를 표시
+ 앱 설정을 관리하기 위한 KeePassDX 속성
+ 필터
+ 순서 지정 없음
+ 제목
+ 사용자명
+ 생성
+ 수정
+ 검색
+ 비밀번호 문자가 데이터베이스 파일의 텍스트 인코딩 범위를 넘는 것을 회피 (인식되지 않는 문자는 동일 글자로 변환).
+ 데이터베이스 변경을 저장하기 위해 파일 쓰기 권한을 허용
+ 이력을 삭제
+ 은행명
+ 토큰값은 %1$d 자리 이상 %2$d 자리 이하가 되어야 합니다.
+ 파일 데이터가 이미 존재합니다.
+ 파일 데이터를 제거하는 중 오류가 발생했습니다.
+ 데이터베이스에 대한 동작 수행 중 오류가 발생했습니다.
+ 데이터 저장
+ 이력을 복구
+ 보조 도메인 제한하에 웹 도메인을 검색
+ 중복에 대해 새로운 UUID를 생성하여 문제를 해결하고 진행할까요\?
+ 앱 속성을 내보냈습니다.
+ 병렬 처리
+ 명령 실행중…
+ 마스터 키 설정
+ OTP 토큰을 표시
+ 그룹에 속한 항목수를 표시
+ 외부 아이콘
+ 고급 잠금 해제
+ 검색 모드
+ 추가적인 암호화 차수를 설정함으로써 무차별 대입 공격(brute force attack)에 대한 방어를 강화할 수 있습니다. 대신 읽기/저장시 느려질 수 있습니다.
+ 앱을 강제 종료하지 마세요.
+ 휴지통을 바닥에
+ 파일에 대한 접근이 파일 관리자에 의해 철회되었습니다.
+ 파손된 데이터페이스 링크를 감춤
+ 앱 속성을 가져오기
+ 앱 속성을 내보내던 중 오류 발생
+ 변환 차수
+ 키 파생 함수에 사용되는 병렬 처리 수준 (즉, 스레드의 갯수)
+ <strong>사용자 데이터를 받아오지 않습니다.</strong> 어떤 서버에도 연결하지 않고 로컬로만 동작하며 사용자의 사생활(프라이버시)를 최우선시합니다.
+ 목록 재구축을 알맞게 할 수 없습니다.
+ 업로드하려는 파일이 너무 큽니다.
+ 요청한 시도로부터 응답을 받을 수 없습니다.
+ 사용자가 취소했습니다.
+ 파일이 훼손되었습니다.
+ 항목수를 표시
+ 항목 목록에서 OTP 토큰을 표시
+ UUID를 표시
+ 항목이나 그룹에 연결된 UUID를 표시
+ 데이터 베이스를 생성
+ 데이터 병합
+ ... 로부터 병합
+ ... 에 복사본 저장
+ 고급 잠금 해제 키를 삭제
+ 등록 모드
+ 접근
+ %1$s 와 동일한 UUID %2$s 가 이미 존재합니다.
+ 밑줄
+ 지원하지 않는 데이터베이스 버전입니다.
\ No newline at end of file
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 526422c40..7a8121f1b 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -59,7 +59,7 @@
Apie
Nustatymai
Duomenų bazės nustatymai
- Išrinti
+ Ištrinti
Aukoti
Keisti
Atidaryti
@@ -82,7 +82,7 @@
Nepavyko atpažinti duomenų bazės formato.
Naturali tvarka
Slėpti slaptažodį
- Programėlės neveiklumas
+ Neveiklumas
Nepavyko išvalyti iškarpinės
Teksto dydis grupės sąraše
KeePassDX negali apdoroti šio URI.
@@ -90,7 +90,7 @@
Rakto failas yra tuščias.
Rakto failas
Įrašo pavadinimas/aprašymas
- Pakeisti master raktą
+ Pakeisti pagrindinį raktą
Naudota
Maskuoti slaptažodį
Tarpas
@@ -103,7 +103,7 @@
Slėpti slaptažodžius pagal nutylėjimą
Neteisingas algoritmas.
Pasirūpininkite, kad kelias būtų teisingas.
- Įveskite slaptažodį ir/ar rakto failą, kad atvertumėte jūsų duomenų bazę.
+ Įveskite slaptažodį ir/ar rakto failą, kad atrakintumėte savo duomenų bazę.
\n
\nSukurkite atsarginę duomenų bazės failo kopiją saugioje vietoje po kiekvieno pakeitimo.
Kai kurie įrenginiai neleidžia programėlėms naudoti iškarpinės.
@@ -116,7 +116,7 @@
Kontaktai
Redaguoti įrašą
Pridėti priedą (failą)
- Duomenų bazės šifravimo algoritmas naudojamas visiems duomenims.
+ Duomenų bazės šifravimo algoritmas naudojamas visiems duomenims
Istorija
Priedai
Pridėti lauką
@@ -131,4 +131,149 @@
Pridėti nodą
Atverti failą
Failų naršyklė, kuri priima „ACTION_CREATE_DOCUMENT“ ir „ACTION_OPEN_DOCUMENT“ veiksmus reikalinga sukurti, atverti ir išsaugoti duomenų bazės failus.
+ Versija
+ Bankas
+ Sėkla
+ Paskyra
+ Banko pavadinimas
+ Saugi pastaba
+ Standartinis
+ Nepavyko sukurti duomenų bazės failo.
+ Įveskite pavadinimą.
+ Šis žodis yra rezervuotas ir negali būti naudojamas.
+ Pasirinkti rakto failą.
+ Nepavyko užkrauti duomenų bazės.
+ Turi būti pasirinktas bent vienas slaptažodžio generavimo tipas.
+ Sukurti naują duomenų bazę
+ Duomenų bazės spalva
+ Nepavyko sukurti failo
+ Nepavyko įkelti rakto. Pabandykite sumažinti KDF \"Atminties naudojimas\".
+ Įrašo piktograma
+ Failo info
+ Pridėti įrašą
+ Fonas
+ Slaptažodžių generatorius
+ Pasirinkti įrašą…
+ Nepavyko įrašyti duomenų bazės.
+ Nepavyko sukurti duomenų bazes su šiuo slaptažodžiu ir rakto failu.
+ Nėra atminties kad užkrauti visą jūsų duomenų bazę.
+ Užrakinti
+ Atrakinkite savo duomenų bazę
+ Užrakinti duomenų bazę
+ Nebegalioja
+ Pasirinkite kad kopijuoti %1$s į iškarpinę
+ Laikotarpis (sekundėmis)
+ OTP tipas
+ Slaptasis raktas turi būti Base32 formato.
+ Laikotarpis turi būti nuo %1$d iki %2$d sekundžių.
+ Čia negalima perkelti grupės.
+ Paslaptis
+ Rodo vartotojų vardus įrašų sąrašuose
+ Failų tvarkyklė panaikino prieigą prie failo, uždarykite duomenų bazę ir vėl ją atidarykite iš jos vietos.
+ Atmesti
+ Nustatyti vienkartinį slaptažodį
+ Skaitiklis
+ Skaičiai
+ Algoritmas
+ OTP
+ Pasirinkite aparatinės įrangos raktą.
+ Neteisinga OTP paslaptis.
+ Čia negalima perkelti įrašo.
+ Čia negalima kopijuoti įrašo.
+ Čia negalima kopijuoti grupės.
+ Žetonas turi būti nuo %1$d iki %2$d skaitmenų.
+ Aparatinės įrangos raktas nėra palaikomas.
+ Nepavyko rasti failo. Pabandykite jį dar kartą atidaryti iš failų naršyklės.
+ Kuriama duomenų bazė…
+ %1$s kopija
+ Formų pildymas
+ Pažangus atrakinimas
+ Saugumo nustatymai
+ Priklausomai nuo jūsų failų tvarkyklės, KeePassDX gali būti neleidžiama rašyti į jūsų saugyklą.
+ Prisiminti aparatinės įrangos raktus
+ Seka naudojamus aparatinės įrangos raktus
+ Failų tvarkyklė panaikino prieigą prie failo
+ Atmesti pakeitimus\?
+ Gaunamas duomenų bazės raktas…
+ Uždaryti laukelius
+ Atšifruojame duomenų bazės turinį…
+ Nepavyko įjungti automatinio pildymo paslaugos.
+ Skaitiklis turi būti nuo %1$d iki %2$d.
+ %1$s su tuo pačiu UUID %2$s jau egzistuoja.
+ Rodyti vartotojų vardus
+ Rodyti įrašų skaičių
+ Rodo grupės įrašų skaičių
+ Įkeliama duomenų bazė…
+ Aparatinės įrangos rakto žymimasis langelis
+ Laukiama iššūkio prašymo…
+ Laukiama atsakymo į iššūkį…
+ Aparatinės įrangos raktas
+ UUID
+ Turi būti nustatytas bent vienas kredencialas.
+ \"Transformacijos raundai\" per dideli. Nustatome į 2147483648.
+ Kiekviena eilutė turi turėti laukelio pavadinimą.
+ Auto-įvedimas
+ CVV
+ Numeris
+ Vardas
+ Įrašo pavadinimas jau egzistuoja.
+ Duomenų bazės URI nepavyko gauti.
+ Rakto išvedimo funkcija
+ KeePassDX © %1$d Kunzisoft yra <strong>atviro kodo</strong> ir <strong>be reklamų</strong>.
+\nJis teikiamas toks, koks yra, pagal <strong>GPLv3</strong> licenciją, be jokių garantijų.
+ Privatus raktas
+ Išdavimo data
+ El. pašto adresas
+ Kriptovaliutos piniginė
+ SWIFT / BIC
+ IBAN
+ Šablonas
+ Išdavimo vieta
+ Įrašyti naują elementą neleidžiama tik skaitymui skirtoje duomenų bazėje
+ Įkeliant failo duomenis įvyko klaida.
+ Įrašo pirmojo plano spalva
+ Įrašo fono spalva
+ Šis tekstas neatitinka prašomo elemento.
+ Vienkartinio slaptažodžio info
+ Failas, kurį bandote įkelti, yra per didelis.
+ Ši forma neatpažįsta esamo OTP tipo, jos patvirtinimas gali nebegeneruoti teisingo žetono.
+ <strong>Jokie naudotojo duomenys nerenkami</strong>, ši programa neprisijungia prie jokio serverio, veikia tik vietoje ir visiškai nepažeidžia naudotojų privatumo.
+ Debit / Kreditinė kortelė
+ Turėtojas
+ El. paštas
+ Narystė
+ Srautinis šifras Arcfour nepalaikomas.
+ XML sugadintas.
+ Nepavyksta tinkamai atkurti sąrašo.
+ Failo duomenys jau egzistuoja.
+ Patvirtinti
+ Laikymo iškarpinėje trukmė (jei tai palaiko jūsų prietaisas)
+ Navigacijos antraštė
+ Atidaryti navigacijos dėklą
+ Uždaryti navigacijos dėklą
+ Šablonai
+ Galima paieška
+ Paveldėti
+ Auto-įvedimo seka
+ Žymės
+ Tinkinti duomenys
+ Paieškos filtrai
+ Dabartinė grupė
+ Didžiųjų ir mažųjų raidžių skirtumas
+ Reguliarioji išraiška
+ PIN
+ ID kortelė
+ Wi-Fi
+ SSID
+ Tipas
+ Žetonas
+ Viešas raktas
+ Pakartotinai perjungti slaptažodžio matomumą
+ Slaptafrazės žodžių skaičius
+ Ši etiketė jau egzistuoja.
+ Atsakymas jau pateiktas.
+ Nepavyko gauti atsakymo į iššūkį.
+ Atšaukė naudotojas.
+ Įvyko klaida šalinant failo duomenis.
+ Įvyko klaida atliekant veiksmą duomenų bazėje.
\ No newline at end of file
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index 6a0ff38eb..c0af5a07f 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -224,7 +224,7 @@
ഇവിടെ ഒരു എൻട്രി നീക്കാൻ കഴിയില്ല.
Autofill service could not be activated.
ക്ലിപ്പ്ബോർഡ് ടൈംഔട്ട്
- ആപ്പ് ടൈംഔട്ട്
+ സമയം അധിക്രമിക്കുക
ഹിസ്റ്ററി വീണ്ടെടുക്കുക
കാലഹരണപ്പെടുന്നു
വൈബ്രേറ്ററി കീപ്രസ്സ്
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index 0cb4e019f..a3e8a6a3a 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -444,7 +444,7 @@
Sett opp OTP
Avbrutt!
Denne teksten samsvarer ikke med det valgte elementet.
- For å beholde vår frihet , fikse feil , legge til funksjoner og være alltid aktiv , stoler vi på ditt bidrag .
+ For å <strong> beholde vår frihet </strong>, <strong> fikse feil </strong>, <strong> legge til funksjoner </strong> og <strong> være alltid aktiv </strong>, stoler vi på ditt <strong> bidrag </strong>.
Gjenta for å skifte passordsynlighet
Egendefinert
Forvalg
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 70d0e0ba3..ef3f792e8 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -19,7 +19,7 @@
Dutch translation by Erik Devriendt, corrected by Erik Jan Meijer
-->
- Reacties
+ Opmerkingen
Startpagina
Android-implementatie van KeePass-wachtwoordbeheer
Accepteren
@@ -27,11 +27,11 @@
Groep toevoegen
Versleutelingsalgoritme
Time-out
- Tijd tot vergrendeling bij inactiviteit
+ Inactieve tijd tot vergrendeling van de database
App
App-instellingen
Haakjes
- Bestandsbeheer dat de Intent-actie ACTION_CREATE_DOCUMENT en ACTION_OPEN_DOCUMENT accepteert, is nodig om databasebestanden aan te maken, te openen en op te slaan.
+ Een bestandsbeheerder die de actie ACTION_CREATE_DOCUMENT en ACTION_OPEN_DOCUMENT accepteert, is vereist om databasebestanden te maken, openen en opslaan.
Klembord gewist
Time-out van het klembord
Duur van opslag op het klembord (indien ondersteund op dit apparaat)
@@ -134,9 +134,9 @@
Toestaan
Klembordfout
Sommige apparaten staan niet toe dat apps het klembord gebruiken.
- Wissen van klembord mislukt
+ Klembord is niet gewist
Geen iteminhoud gevonden.
- Je database kan niet worden geladen.
+ De database is niet geladen.
De sleutel kan niet worden geladen. Probeer om het \"geheugengebruik\" van KDF te verminderen.
Elke zin moet een veldnaam bevatten.
De dienst automatisch aanvullen kan niet worden ingeschakeld.
@@ -160,7 +160,7 @@
KeePassDX moet worden gemachtigd om je databank te kunnen aanpassen.
Bestandsgeschiedenis
Toon locaties van recente databases
- Database-encryptie-algoritme dat voor alle gegevens wordt gebruikt.
+ Database-versleutelingsalgoritme voor alle gegevens
Om de sleutel voor het algoritme te kunnen genereren, wordt de hoofdsleutel getransformeerd middels een willekeurige afleidingsfunctie.
Geheugengebruik
De hoeveelheid geheugen dat de afleidingsfunctie mag gebruiken.
@@ -169,7 +169,7 @@
Sorteren
Laagste eerst ↓
Groepen vooraan plaatsen
- Prullenbak onderaan plaatsen
+ Prullenbak onderaan
Titel
Gebruikersnaam
Gecreëerd op
@@ -190,7 +190,7 @@
Dienst automatisch aanvullen
Schakel de dienst in om formulieren in andere apps in te vullen
Gegenereerde wachtwoordlengte
- Stel de standaardlengte van gegenereerd wachtwoord in
+ Stel de standaardlengte van gegenereerde wachtwoorden in
Wachtwoordtekens
Toegestane wachtwoordtekens instellen
Klembord
@@ -311,7 +311,7 @@
Vergrendel de database wanneer de gebruiker in het hoofdscherm op de knop Terug klikt
Wissen bij afsluiten
Vergrendel de database wanneer de duur van het klembord verloopt of de melding wordt gesloten nadat u deze bent gaan gebruiken
- Prullenmand
+ Prullenbak
Itemselectie
Vul, bij het bekijken van een item in KeePassDX, Magikeyboard met dat item
Wachtwoord wissen
@@ -322,8 +322,8 @@
Item toevoegen
Groep toevoegen
Bestandsinformatie
- Wachtwoord-selectie
- Sleutelbestand-selectie
+ Wachtwoord-selectievak
+ Sleutelbestand-selectievak
Weergave van het wachtwoord wisselen
Item-pictogram
Wachtwoordgenerator
@@ -403,7 +403,7 @@
Download %1$s
Stel eenmalig wachtwoordbeheer (HOTP / TOTP) in om een token te genereren voor tweefactorauthenticatie (2FA).
Automatisch opslaan
- Prullenbak
+ Prullenbak-groep
Toont de vergrendelknop in de gebruikersinterface
Vergrendelknop tonen
Instellingen voor automatisch aanvullen
@@ -413,7 +413,7 @@
Bestandstoegang verlenen om databasewijzigingen op te slaan
Opdracht uitvoeren…
Gebroken links in de lijst met recente databases verbergen
- Corrupte databasekoppelingen verbergen
+ Verbroken databasekoppelingen verbergen
Onthoud de locatie van databasesleutelbestanden
Onthoud de locatie van databases
Zoekopdracht aanmaken bij het openen van een database
@@ -422,7 +422,7 @@
Geschiedenis herstellen
Prullenbak legen
Gegevens opslaan
- Kan database niet opslaan.
+ De database is niet opgeslagen.
Databasebestand is niet aangemaakt.
Dit label bestaat al.
Bijlagen
@@ -431,7 +431,7 @@
Weggooien
Veranderingen ongedaan maken\?
Valideren
- Bijdragen
+ Donatie
Contact
Start de app met het formulier opnieuw op om de blokkering te activeren.
Blokkering van automatisch invullen
@@ -608,7 +608,7 @@
Houd het scherm aan
Houd het scherm aan bij het bekijken van een item
Itemkleuren
- Toont voorgrond- en achtergrondkleuren in een item
+ Toont items met voorgrond- en achtergrondkleuren
Wi-Fi
Auto-Type tekenreeks
Verlopen
@@ -646,4 +646,28 @@
Tekens negeren
Woordhoofdletters
Aantal tekens: %1$d
+ Navigatie-kop
+ Fysieke sleutel
+ Misvormde XML.
+ Reactietest-vraag is al uitgegeven
+ Reactietest-antwoord is al geleverd.
+ Geen antwoord gekregen op de reactietest.
+ Geannuleerd door gebruiker.
+ Stuurprogramma vereist voor %1$s.
+ Sleutel mag niet leeg zijn.
+ Beschadigd bestand.
+ Fysieke sleutels onthouden
+ Schermopname-modus
+ Toestaan dat andere apps schermopnames maken van deze app
+ Schermopname-modus
+ Auto-Type
+ Samenvoegen vanuit database V1 niet mogelijk.
+ Kies een fysieke sleutel.
+ Houdt de gebruikte fysieke sleutels bij
+ <strong> Er worden geen gebruikersgegevens opgehaald </strong>, deze applicatie maakt geen verbinding met een server, werkt alleen lokaal en respecteert volledig de privacy van gebruikers.
+ Locatie van database is onbekend, actie kan niet worden uitgevoerd.
+ Fysieke sleutel-selectievak
+ In afwachting van de reactietest-vraag…
+ In afwachting van het reactietest-antwoord…
+ Deze fysieke sleutel wordt niet ondersteund.
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index fb873d104..c85cd745c 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with KeePassDX. If not, see .
-->
- Informacje zwrotne
+ Zgłoś błąd lub sugestię
Strona główna
Implementacja menedżera haseł KeePass w systemie Android
Akceptuj
@@ -27,7 +27,7 @@
Koniec czasu
Czas bezczynności przed zablokowaniem bazy danych
Aplikacja
- Ustawienia aplikacji
+ Ustawienia ogólne
Nawiasy
Do tworzenia, otwierania i zapisywania plików bazy danych potrzebny jest menedżer plików, który akceptuje działanie Intent Action ACTION_CREATE_DOCUMENT i ACTION_OPEN_DOCUMENT.
Schowek został wyczyszczony
@@ -37,7 +37,7 @@
Tworzenie klucza bazy danych…
Baza danych
Odszyfrowywanie zawartości bazy danych…
- Użyj jako domyślnej bazy danych
+ Ustaw jako domyślną bazę danych
Cyfry
Otwórz istniejącą bazę danych
Dostęp do pliku
@@ -82,13 +82,13 @@
Wczytywanie bazy danych…
Małe litery
Ukryj hasła
- Maskuj hasła (***) domyślnie
- Informacje
+ Domyślnie maskuj hasła (***)
+ O aplikacji
Zmień klucz główny
Ustawienia
Ustawienia bazy danych
Usuń
- Dotuj
+ Wspomóż
Edytuj
Ukryj hasło
Zablokuj bazę danych
@@ -108,7 +108,7 @@
Dodatkowe rundy szyfrowania zapewniają lepszą ochronę przed atakami typu brute force, ale mogą znacznie spowolnić ładowanie i zapisywanie.
Zapisywanie bazy danych…
Spacja
- Porządek naturalny
+ Porządek domyślny
Znaki specjalne
Szukaj
Podkreślenie
@@ -153,29 +153,29 @@
Chroniony przed zapisem
Modyfikowalne
Ochrona
- Chroniony przed zapisem
+ Ochrona przed zapisem
KeePassDX potrzebuje uprawnień do zapisu, aby mógł modyfikować bazę danych.
- Algorytm szyfrowania bazy danych używany dla wszystkich danych.
+ Algorytm szyfrowania bazy danych używany dla wszystkich danych
Aby wygenerować klucz dla algorytmu szyfrowania, klucz główny jest transformowany przy użyciu losowo solonej funkcji wyprowadzania klucza.
Użycie pamięci
Ilość pamięci do użycia przez funkcję wyprowadzania klucza.
Równoległość
Stopień równoległości (tj. liczba wątków) wykorzystywany przez funkcję wyprowadzania klucza.
Sortuj
- Od najniższej ↓
+ Rosnąco ↓
Nazwa użytkownika
Utworzenie
Modyfikacja
Dostęp
Ostrzeżenie
Unikaj znaków hasła spoza formatu kodowania tekstu w pliku bazy danych (nierozpoznane znaki są konwertowane na tę samą literę).
- Kosz na dole
+ Pokaż Kosz na dole listy
Tytuł
Kontynuować bez ochrony odblokowującej hasło\?
Kontynuować bez klucza szyfrowania\?
Wersja %1$s
Zapisano zaszyfrowane hasło
- Grupy poprzednie
+ Pokaż Grupy na górze listy
Baza danych nie ma jeszcze hasła.
Historia
Wygląd
@@ -190,10 +190,10 @@
Znaki hasła
Ustaw dozwolone znaki generatora haseł
Schowek
- Powiadomienia ze schowka
+ Powiadomienia schowka
Pokaż powiadomienia schowka, aby skopiować pola podczas przeglądania wpisu
Jeśli automatyczne usuwanie schowka nie powiedzie się, ręcznie usuń jego historię.
- Blokada
+ Zablokuj bazę danych
Blokada ekranu
Zablokuj bazę danych po kilku sekundach od wyłączenia ekranu
Zaawansowane odblokowywanie
@@ -211,8 +211,8 @@
Wykorzystaj kosz
Przenosi grupy i wpisy do grupy \"Kosz\" przed usunięciem
Czcionka pola
- Zmień czcionkę użytą w polach, aby poprawić widoczność postaci
- Zaufanie do schowka
+ Zmień czcionkę użytą w polach, aby poprawić widoczność znaków
+ Ochrona schowka
Zezwalanie na kopiowanie hasła wejściowego i chronionych pól do schowka
Ostrzeżenie: Schowek jest współużytkowany przez wszystkie aplikacje. Jeśli poufne dane są kopiowane, inne oprogramowanie może je odzyskać.
Nazwa bazy danych
@@ -265,19 +265,19 @@
Wybierz sposób sortowania wpisów i grup.
Weź udział
Pomóż zwiększyć stabilność, bezpieczeństwo i dodawanie kolejnych funkcji.
- W przeciwieństwie do wielu aplikacji do zarządzania hasłami, ta jest wolna od reklam, jest wolnym oprogramowaniem objętym klauzulą copyleft i nie zbiera danych osobowych na swoich serwerach, bez względu na to, jakiej wersji używasz.
+ W przeciwieństwie do wielu aplikacji do zarządzania hasłami, ta jest wolna od <strong>reklam</strong>, <strong>jest wolnym oprogramowaniem objętym klauzulą copyleft</strong> i nie zbiera danych osobowych na swoich serwerach, bez względu na to, jakiej wersji używasz.
Kupując wersję pro, będziesz mieć dostęp do <strong>stylu wizualnego</strong> a szczególnie pomożesz <strong> zrealizować projekty społecznościowe.</strong>
Ten <strong>styl wizualny</strong> jest dostępny dzięki Twojej hojności.
Aby zachować naszą wolność i być zawsze aktywnym, liczymy na Twój <strong>wkład.</strong>
- Ta funkcja jest rozwojowa i wymaga Twojego wkładu, aby być wkrótce dostępną.
- Kupując wersję pro,
+ Ta funkcja jest <strong>rozwojowa</strong> i wymaga Twojego <strong>wkładu</strong>, aby być wkrótce dostępną.
+ Kupując wersję <strong>pro</strong>,
Przez <strong>przyczynianie się</strong>,
zachęcasz programistów do tworzenia <strong>nowych funkcji</strong> i <strong>naprawy błędów</strong> zgodnie z Twoimi uwagami.
Wielkie dzięki za Twój wkład.
Ciężko pracujemy, aby szybko udostępnić tę funkcję.
Pamiętaj, aby aktualizować swoją aplikację, instalując nowe wersje.
Pobieranie
- Przyczyń się
+ Wspomóż projekt
Motyw aplikacji
Motyw używany w aplikacji
Pakiet ikon
@@ -365,12 +365,12 @@
Zaleca się zmianę klucza głównego (dni)
Wymagaj zmiany klucza głównego (dni)
Domyślna nazwa użytkownika
- Niestandardowy kolor bazy danych
+ Własny kolor bazy danych
Kompresja
Żadna
Gzip
Ustawienia klawiatury urządzenia
- Nieprawidłowy klucz tajny OTP.
+ Nieprawidłowy klucz OTP.
Należy ustawić co najmniej jedno poświadczenie.
Klucz tajny musi być w formacie Base32.
Licznik musi być między %1$d a %2$d.
@@ -382,7 +382,7 @@
Użyj zaawansowanego odblokowywania w celu łatwiejszego otwierania bazy danych
Kompresja danych zmniejsza rozmiar bazy danych
Maksymalna liczba
- Ogranicz rozmiar historii na wpis
+ Ogranicz rozmiar historii każdego wpisu
Wymuś odnowienie
Wymuś odnowienie następnym razem
Wymagaj zmiany klucza głównego następnym razem (raz)
@@ -404,23 +404,23 @@
Inicjowanie…
W trakcie realizacji: %1$d%%
Kończę…
- Kompletny!
+ Ukończono!
Ukryj wygasłe wpisy
Wygasłe wpisy nie są wyświetlane
Kontakt
- Aby zachować naszą wolność, sprawdzać błędy, dodawać funkcje i być zawsze aktywnym, liczymy na Twój wkład.
+ Aby <strong>zachować naszą wolność</strong>, <strong>naprawiać błędy</strong>, <strong>dodawać funkcje</strong> i <strong> być zawsze aktywnym</strong>, liczymy na Twój <strong>wkład</strong>.
Szybkie wyszukiwanie
Wyszukiwanie po otwarciu bazy danych
Zapamiętaj lokalizacje baz danych
Śledzi, gdzie przechowywane są bazy danych
Zapamiętaj lokalizacje plików kluczy
Śledzi, gdzie przechowywane są pliki z kluczami
- Pokaż najnowsze pliki
+ Pokaż ostatnie pliki
Pokaż lokalizacje najnowszych baz danych
Ukryj uszkodzone łącza do bazy danych
Ukryj uszkodzone łącza na liście najnowszych baz danych
Przyznaj dostęp do zapisu pliku, aby zapisać zmiany w bazie danych
- Wkład
+ Wsparcie finansowe
Skonfiguruj zarządzanie hasłem jednorazowym (HOTP / TOTP), aby wygenerować token wymagany do uwierzytelniania dwuskładnikowego (2FA).
Konfiguracja OTP
Nie można utworzyć pliku bazy danych.
@@ -451,7 +451,7 @@
Przełącz klawiaturę
Prześlij %1$s
Prześlij załącznik do wpisu, aby zapisać ważne dane zewnętrzne.
- Dodawanie załącznika
+ Dodaj załącznik
Czy mimo to dodać plik\?
Przesłanie tego pliku spowoduje zastąpienie istniejącego.
Baza danych KeePass powinna zawierać tylko małe pliki narzędziowe (takie jak pliki kluczy PGP).
@@ -467,7 +467,7 @@
Informacje o poświadczeniach
Wyświetla identyfikator UUID powiązany z wpisem lub grupą
Pokaż UUID
- Zapisywanie danych nie jest dozwolone dla bazy danych otwartej jako tylko do odczytu.
+ Zapisywanie danych nie jest dozwolone dla bazy danych otwartej tylko do odczytu.
Pytaj o zapisanie danych po zakończeniu wypełniania formularza
Pytaj o zapisanie danych
Staraj się zapisywać informacje wyszukiwania podczas dokonywania ręcznego wyboru wpisu, aby ułatwić sobie przyszłe użycie
@@ -497,7 +497,7 @@
Odblokowywanie uwierzytelniające urządzenia
Uwierzytelnienie urządzenia
Wpisz hasło, a następnie kliknij ten przycisk.
- Nie można rozpoznać zaawansowanego wydruku odblokowującego
+ Nie można rozpoznać wprowadzonych danych odblokowujących
Nie można odczytać zaawansowanego klucza odblokowującego. Usuń go i powtórz procedurę rozpoznawania odblokowania.
Wyodrębnij poświadczenia bazy danych z zaawansowanymi danymi odblokowującymi
Jeśli używasz zaawansowanego rozpoznawania odblokowania, nadal musisz zapamiętać główne dane uwierzytelniające.
@@ -523,9 +523,9 @@
Otwórz monit odblokowania zaawansowanego, aby przechowywać poświadczenia
Otwórz monit odblokowania zaawansowanego, aby odblokować bazę danych
Dostęp do pliku odwołany przez menedżera plików, zamknij bazę danych i otwórz ją ponownie z jej lokalizacji.
- Scal dane, zastąp modyfikacje zewnętrzne poprzez zapisanie bazy danych lub ponownie załaduj ją z najnowszymi zmianam.
+ Scal dane, zastąp modyfikacje zewnętrzne poprzez zapisanie bazy danych lub ponownie załaduj ją z najnowszymi zmianami.
Informacje zawarte w pliku bazy danych zostały zmodyfikowane poza aplikacją.
- Ponownie załaduj dane
+ Przeładuj dane
GiB
MiB
KiB
@@ -543,13 +543,13 @@
Dane pliku już istnieją.
Właściwości
Błąd podczas eksportowania właściwości aplikacji
- Wyeksportowano właściwości aplikacji
+ Eksportowano właściwości aplikacji
Błąd podczas importowania właściwości aplikacji
- Zaimportowano właściwości aplikacji
+ Importowano właściwości aplikacji
Właściwości KeePassDX do zarządzania ustawieniami aplikacji
- Utwórz plik, aby wyeksportować właściwości aplikacji
+ Utwórz plik, aby eksportować właściwości aplikacji
Eksportuj właściwości aplikacji
- Wybierz plik, aby zaimportować właściwości aplikacji
+ Wybierz plik, aby importować właściwości aplikacji
Importuj właściwości aplikacji
Wystąpił błąd podczas wykonywania akcji w bazie danych.
Nie możesz tutaj przenieść grupy.
@@ -588,7 +588,7 @@
Posiadacz
Karta debetowa / kredytowa
Szablony
- Wyświetla tokeny OTP na liście wpisów
+ Pokaż tokeny OTP na liście wpisów
Pokaż token OTP
Ikona zewnętrzna
Wyświetl opcję pozwalającą użytkownikowi wybrać wpis bazy danych
@@ -605,9 +605,9 @@
Kolory wpisu
Hash pliku nie jest gwarantowany, ponieważ system Android może zmieniać swoje dane w locie. Zmień rozszerzenie pliku na .bin, aby uzyskać prawidłową integralność.
Nie wyłączaj ekranu podczas oglądania wpisu
- Znaczniki
+ Etykiety
Nie wyłączaj ekranu
- Wyświetla kolory pierwszego planu i tła we wpisie
+ Wyświetla kolory pierwszego planu i tła wpisu
Nagłówek nawigacyjny
Szuflada nawigacyjna otwarta
Szuflada nawigacyjna zamknięta
@@ -617,11 +617,11 @@
Sekwencja automatycznego wpisywania
Obecna grupa
Dane niestandardowe
- Scal z…
+ Scal z …
Filtry wyszukiwania
Automatyczne wpisywanie
Przeszukiwalne
- Zapisz kopię w…
+ Zapisz kopię w …
Wygasłe
Baza danych jest już otwarta, należy ją najpierw zamknąć, aby otworzyć nową
Ta funkcja umożliwia przechowywanie zaszyfrowanych danych uwierzytelniających w bezpiecznym magazynie kluczy urządzenia.
@@ -646,4 +646,26 @@
Ignoruj znaki
Koloruj hasła
Automatycznie przełączaj z powrotem do poprzedniej klawiatury na ekranie wyszukiwania
+ Oczekiwanie na żądanie wyzwania…
+ Oczekiwanie na odpowiedź na wyzwanie…
+ Wyzwanie już zażądane
+ Odpowiedź już udzielona.
+ Anulowano przez użytkownika.
+ Nie można scalić z bazą danych V1.
+ Lokalizacja bazy danych jest nieznana, nie można wykonać działania bazy danych.
+ Klucz sprzętowy nie jest obsługiwany.
+ Klucz nie może być pusty.
+ Uszkodzony plik.
+ Zapamiętaj klawisze sprzętowe
+ Śledzi używane klucze sprzętowe
+ Tryb zrzutu ekranu
+ Zezwól aplikacjom innych dostawców na nagrywanie lub robienie zrzutów ekranu aplikacji
+ Klucz sprzętowy
+ Nie można uzyskać odpowiedzi na wyzwanie.
+ Wymagany jest sterownik do %1$s.
+ Pole wyboru klucza sprzętowego
+ Wybierz klucz sprzętowy.
+ Zniekształcony XML.
+ Tryb zrzutu ekranu
+ <strong>Żadne dane użytkownika nie są pobierane</strong>, ta aplikacja nie łączy się z żadnym serwerem, działa tylko lokalnie i w pełni szanuje prywatność użytkowników.
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 7d7d87ccb..8a08a7665 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -153,7 +153,7 @@
KeePassDX precisa de permissões de escrita para poder mudar qualquer coisa no seu banco.
Exibir arquivos recentes
Exibir locais de bancos de dados recentes
- Algoritmo de criptografia usado para todos os dados.
+ Algoritmo de criptografia usado para todos os dados
Para gerar uma chave para o algoritmo de criptografia, a chave-mestra é transformada usando uma função de derivação de chave salgada aleatoriamente.
Uso de memória
Quantidade de memória a ser usada pela função de derivação de chave.
@@ -210,7 +210,7 @@
Muda a fonte usada nos campos para melhor visibilidade dos caracteres
Confiança na área de transferência
Permitir a cópia da senha e de campos protegidos para a área de transferência
- AVISO: A área de transferência é compartilhada por todos os aplicativos. Se dados sensíveis forem copiados, outros programas podem recuperá-los.
+ AVISO: A área de transferência é compartilhada por todos os aplicativos. Se dados sensíveis forem copiados, outros programas podem acessá-los.
Nome do banco de dados
Descrição do banco de dados
Versão do banco de dados
@@ -220,10 +220,10 @@
Teclado
Magikeyboard
Ative um teclado customizado, populando suas senhas e todos os campos de identidade
- Não permitir chave-mestra
- Permitir tocar no botão \"Abrir\" se nenhuma credencial for selecionada
+ Permitir chave-mestra vazia
+ Permite tocar no botão \"Abrir\" mesmo se nenhuma credencial for selecionada
Somente leitura
- Por padrão, abrir seu banco de dados no modo somente leitura
+ Abre o banco de dados no modo somente leitura por padrão
Dicas educacionais
Destaque os elementos para aprender como o aplicativo funciona
Reiniciar telas educacionais
@@ -279,7 +279,7 @@
Pacote de ícones
Pacote de ícones usado no aplicativo
Editar entrada
- Não foi possível carregar seu banco de dados.
+ Não foi possível carregar o banco de dados.
Não pôde carregar a chave. Tente diminuir o \"Uso de Memória\" do KDF.
Mostrar nomes de usuário
Exibe nomes de usuário em listas de entrada
@@ -305,8 +305,8 @@
Teclas audíveis
Modo seleção
Não feche o aplicativo…
- Pressione \'Voltar\' para bloquear
- Trancar a base de dados quando o usuário pressiona o botão \"Voltar\" na tela inicial
+ Pressionar \'Voltar\' para bloquear
+ Tranca o banco de dados quando o usuário pressiona o botão \"Voltar\" na tela inicial
Limpar ao fechar
Bloquear o banco de dados quando a duração da área de transferência expirar ou a notificação for fechada depois do início do uso
Lixeira
@@ -368,8 +368,8 @@
O banco de dados contém UUIDs duplicados.
Consertar o problema gerando nova UUIDs para duplicatas para continuar\?
Banco de dados aberto
- Copiar campos de entrada usando a área de transferência do seu aparelho
- Usar destravamento avançado para abrir o banco de dados mais facilmente
+ Copie campos de entrada usando a área de transferência do seu dispositivo
+ Use o desbloqueio avançado para abrir o banco de dados mais facilmente
Compressão dos dados
Compressão dos dados reduz o tamanho do banco de dados
Número máximo
@@ -388,7 +388,7 @@
Nada
Gzip
Configurações do teclado do aparelho
- Não foi possível salvar no banco de dados.
+ Não foi possível salvar o banco de dados.
Salvar dados
Esvaziar lixeira
Executando o comando…
@@ -396,7 +396,7 @@
O armazenamento chaves não foi propriamente inicializado.
Grupo de lixeira
Salvar automaticamente o banco de dados
- Salvar automaticamente o banco de dados depois de uma ação importante (somente no modo \"Modificável\")
+ Salva automaticamente o banco de dados após cada ação importante (somente no modo \"Modificável\")
Descartar
Contato
Busca rápida
@@ -428,7 +428,7 @@
Não foi possível criar o arquivo do banco de dados.
Este rótulo já existe.
Anexos
- Para manter nossa liberdade, consertar erros, adicionar recursos e para estar sempre ativo, contamos com sua contribuição.
+ Para <strong>manter nossa liberdade</strong>, <strong>consertar erros</strong>, <strong>adicionar recursos</strong> e <strong>para estar sempre ativo</strong>, contamos com sua <strong>contribuição</strong>.
Adicionar anexo
Descartar mudanças\?
Validar
@@ -604,7 +604,7 @@
Mantenha a tela ligada
Mantenha a tela ligada enquanto estiver vendo a entrada
Cores de entrada
- Exibe as cores de primeiro plano e de fundo em uma entrada
+ Exibe as cores de primeiro plano e de fundo de uma entrada
Você não pode mover um grupo pra cá.
Wi-Fi
O hash do arquivo não é garantido porque o Android pode alterar seus dados em tempo real. Altere a extensão do arquivo para .bin para obter a integridade correta.
@@ -625,14 +625,14 @@
Número de palavras da senha
Um banco de dados já está aberto, feche-o primeiro para abrir o novo
Tela de busca
- Entropia: Calcular…
+ Entropia: Calculando…
Pelo menos um caractere de cada
Excluir caracteres ambíguos
- Considerar caracteres
+ Considere caracteres
MAIÚSCULAS
Capitalização de Título
Colorir senhas
- Colorir caracteres da senha por tipo
+ Colorir caracteres de senha por tipo
Frase secreta
Entropia: %1$s bit
Entropia: Alta
@@ -644,5 +644,28 @@
Ignorar caracteres
Separador
minúsculas
- Número de caracteres: %1$d
+ Contagem de caracteres: %1$d
+ Aguardando o pedido de desafio…
+ Aguardando a resposta do desafio…
+ Cancelado pelo usuário.
+ A chave física não é suportada.
+ Não é possível mesclar a partir de um banco de dados V1.
+ Localização do banco de dados desconhecida, a ação do banco de dados não pode ser executada.
+ A chave não pode estar vazia.
+ <strong>Nenhum dado do usuário é coletado</strong>, este aplicativo não se conecta a nenhum servidor, funciona apenas localmente e respeita totalmente a privacidade dos usuários.
+ Chave física
+ Selecione uma chave física.
+ XML malformado.
+ Desafio já solicitado
+ Resposta já fornecida.
+ Não foi possível obter a resposta do desafio.
+ É necessário o driver para %1$s.
+ Lembrar chaves físicas
+ Mantém um registro das chaves físicas utilizadas
+ Modo de captura de tela
+ Permitir que aplicativos de terceiros gravem ou façam capturas de tela do aplicativo
+ Modo de captura de tela
+ Arquivo corrompido.
+ Preenchimento automático
+ Caixa de verificação da chave física
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 0e0fbca2d..32822591f 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -42,8 +42,8 @@
A desencriptar o conteúdo da base de dados…
Utilizar como base de dados predefinida
Dígitos
- KeePassDX © %1$d Kunzisoft tem o código-fonte aberto e sem publicidade.
-\nÉ fornecido como está, sob a licença GPLv3, sem qualquer garantia.
+ KeePassDX © %1$d Kunzisoft tem o <strong>código-fonte aberto</strong> e <strong>sem publicidade</strong>.
+\nÉ fornecido como está, sob a <strong>licença GPLv3</strong>, sem qualquer garantia.
Abrir uma base de dados existente
Acedido
Cancelar
@@ -142,7 +142,7 @@
ASCII estendido
Permitir
- Não foi possível abrir a sua base de dados.
+ Não foi possível abrir a base de dados.
Não foi possível carregar a chave. Tente diminuir o \"Uso de memória\" do KDF.
Mostrar nomes de utilizador
Cópia de %1$s
@@ -186,7 +186,7 @@
Encriptação
Função de derivação de chave
Não foi possível ativar o serviço de preenchimento automático.
- Algoritmo de encriptação usado para todos os dados.
+ Algoritmo de encriptação usado para todos os dados
Define o tamanho predefinido para as palavras-passe geradas
Caracteres das palavras-passe
Definir os caracteres permitidos para o gerador de palavras-passe
@@ -246,14 +246,14 @@
Escolha como são ordenadas as entradas e os grupos.
Participar
Ajude a aumentar a estabilidade, segurança e novas funcionalidades.
- Ao contrário de muitas aplicações de gestão de palavras-passe, esta aplicação não tem anúncios, é um programa de código-fonte livre e não recolhe dados pessoais para os servidores dela, seja qual for a versão que utiliza.
- Ao comprar a versão pro, terá acesso a este estilo visual e ajudará especialmente a realizar projetos comunitários.
+ Ao contrário de muitas aplicações de gestão de palavras-passe, esta aplicação <strong>não tem anúncios</strong>, é <strong>um programa de código-fonte livre</strong> e não recolhe dados pessoais para os servidores dela, seja qual for a versão que utiliza.
+ Ao comprar a versão pro, terá acesso a este <strong>estilo visual</strong> e ajudará especialmente a <strong>realizar projetos comunitários.</strong>
Este <strong>estilo visual</strong> está disponível graças à sua generosidade.
- Para manter a nossa liberdade e estarmos sempre ativos, contamos com a sua contribuição.
- Esta funcionalidade está em desenvolvimento e necessita da sua contribuição para que esteja disponível brevemente.
+ Para manter a nossa liberdade e estarmos sempre ativos, contamos com a sua <strong>contribuição.</strong>
+ Esta funcionalidade está <strong>em desenvolvimento</strong> e necessita da sua <strong>contribuição</strong> para que esteja disponível brevemente.
Ao comprar a versão <strong>pro</strong>,
- Ao contribuir,
- está a incentivar os programadores a adicionar novas funcionalidades e a corrigir erros reportados pelos utilizadores.
+ Ao <strong>contribuir</strong>,
+ está a incentivar os programadores a adicionar <strong>novas funcionalidades</strong> e a <strong>corrigir erros</strong> reportados pelos utilizadores.
Obrigado pela sua contribuição.
Estamos a trabalhar para lançar esta funcionalidade o mais rápido possível.
Lembre-se de manter a sua aplicação atualizada, instalando novas versões.
@@ -276,7 +276,7 @@
\n
\n\"Apenas leitura\" evita que faça alterações não intencionais na base de dados.
\n\"Alterável\" permite adicionar, eliminar ou alterar todos os elementos.
- Mostrar nomes de utilizador na lista entradas
+ Mostra nomes de utilizador nas listas de entradas
Área de transferência
Magikeyboard
Magikeyboard (KeePassDX)
@@ -299,7 +299,7 @@
Modo de seleção
Não feche a aplicação…
Pressione \'Voltar\' para bloquear
- Tranca a base de dados quando o utilizador pressiona o botão voltar no ecrã inicial
+ Bloquear a base de dados quando o utilizador pressiona o botão voltar no ecrã inicial
Limpar ao fechar
Bloquear a base de dados quando a duração da área de transferência expirar ou quando a notificação for fechada depois de começar a utilizá-la
Caixote da reciclagem
@@ -325,14 +325,14 @@
Não se pode mover uma entrada para aqui.
Não se pode copiar uma entrada aqui.
Mostrar número de entradas
- Mostrar o número de entradas dentro de um grupo
+ Mostra o número de entradas dentro de um grupo
Cor personalizada da base de dados
A compressão de dados reduz o tamanho da base de dados
Permite que leia os seus dados biométricos para abrir a base de dados
Usar desbloqueio avançado para abrir a base de dados mais facilmente
Base de dados aberta
A base de dados contém UUIDs duplicados.
- Guardar base de dados
+ Guardar dados
A criar a base de dados…
Não foi possível guardar a base de dados.
Não foi possível criar a base de dados com essa palavra-passe e ficheiro-chave.
@@ -380,7 +380,7 @@
A chave secreta tem de ser no formato Base32.
Ativar
Compressão de dados
- Para manter a liberdade, corrigir erros, adicionar funcionalidades e para sermos sempre ativos, contamos com sua contribuição.
+ Para <strong>manter a liberdade</strong>, <strong>corrigir erros</strong>, <strong>adicionar funcionalidades</strong> e <strong>para sermos sempre ativos</strong>, contamos com sua <strong>contribuição</strong>.
Desbloqueio biométrico
Anexos
Gzip
@@ -473,26 +473,26 @@
Não foi possível ler a chave de desbloqueio avançada. Por favor, elimine-a e repita o procedimento de reconhecimento de desbloqueio.
Extrair credencial da base de dados com dados de desbloqueio avançados
Abrir base de dados com reconhecimento de desbloqueio avançado
- Advertência: ainda tem de se lembrar da sua palavra-passe principal se usar o reconhecimento de desbloqueio avançado.
+ Ainda terá de se lembrar da sua credencial principal se usar o reconhecimento de desbloqueio avançado.
Reconhecimento de desbloqueio avançado
Abrir o alerta de desbloqueio avançado para armazenar as credenciais
Abrir o alerta de desbloqueio avançado para desbloquear a base de dados
É necessária uma atualização de segurança biométrica.
Não está registada nenhuma credencial biométrica ou de dispositivo.
Acesso ao ficheiro revogado pelo gestor de ficheiros. Feche a base de dados e reabra-a a partir da sua localização.
- Substitua as alterações externas, guardando a base de dados ou recarregando-a com as últimas alterações.
+ Unir os dados, substituir as alterações externas guardado a base de dados ou recarregá-los com as últimas alterações.
A informação contida no seu ficheiro da base de dados foi alterada fora da aplicação.
Eliminar permanentemente todos os nós do caixote da reciclagem\?
Modo de registo
Modo de guardar
Modo de pesquisa
Eliminar chave de desbloqueio avançada
- Recarregar base de dados
+ Recarregar dados
Não foi possível reconstruir adequadamente a lista.
Não foi possível recuperar o URI da base de dados.
O nome do campo já existe.
Não é permitido guardar um novo item numa base de dados só de leitura
- Informações sobre a palavra-passe de uso único
+ Informação de palavra-passe de uso único
Mostra os tokens OTP na lista de entradas
Mostrar token OTP
Mostra o UUID ligado a uma entrada ou grupo
@@ -510,9 +510,9 @@
Desbloqueio avançado da base de dados
Adicionadas sugestões de preenchimento automático.
Não é possível guardar dados numa base de dados aberta apenas com permissão de leitura.
- Pedir para guardar dados quando é validado um formulário
+ Pedir para guardar dados quando terminar de preencher um formulário
Pedir para guardar dados
- Tentar guardar as informações de pesquisas ao fazer uma seleção de entrada manual
+ Tentar guardar as informações de pesquisas ao fazer uma seleção de entrada manual para facilitar utilizações posteriores
Guardar informações de pesquisas
Mostrar opção para permitir que o utilizador selecione a entrada da base de dados
Seleção manual
@@ -527,7 +527,7 @@
Campos personalizados
Mudar automaticamente para o teclado anterior após bloquear a base de dados
Bloquear base de dados
- Depois de partilhar um URL com o KeePassDX, quando uma entrada for selecionada, memorizar essa entrada para utilizações posteriores
+ Tentar guardar informação partilhada ao fazer uma seleção manual de entradas para uma utilização futura mais fácil
Guardar informação partilhada
Modelos
Notificação
@@ -592,4 +592,80 @@
Desbloqueio das credenciais do dispositivo
Tocar para as eliminar chaves de desbloqueio avançado
Conteúdo
+ Expirou
+ Frase-chave
+ Manter o ecrã ligado
+ Não se pode mover um grupo para aqui.
+ Caixa de verificação da chave física
+ Cor do primeiro plano da página inicial
+ Número de palavras da frase-chave
+ À espera do pedido de desafio…
+ À espera da resposta ao desafio…
+ <strong>Nenhum dado de utilizador é recuperado</strong>, esta aplicação não se liga a nenhum servidor, funciona apenas localmente e respeita plenamente a privacidade dos utilizadores.
+ Etiquetas
+ Filtros de pesquisa
+ Wi-Fi
+ Desafio já solicitado
+ Resposta já fornecida.
+ Não foi possível obter a resposta do desafio.
+ Não é possível fundir a partir de uma base de dados V1.
+ A localização da base de dados é desconhecida, a ação da base de dados não pode ser executada.
+ A chave física não é suportada.
+ A chave não pode estar vazia.
+ Ficheiro corrompido.
+ Colorir palavras-passe
+ Mantém um registo das chaves físicas utilizadas
+ Já está aberta uma base de dados, feche-a primeiro para poder abrir uma nova
+ Recarregar a base de dados irá eliminar os dados alterados localmente.
+ O hash do ficheiro não é garantido porque o Android pode alterar os seus dados em tempo real. Altere a extensão do ficheiro para .bin para obter a integridade correta.
+ Esta funcionalidade irá armazenar dados encriptados de credenciais na KeyStore segura do seu dispositivo.
+\n
+\nDependendo da implementação da API nativa do sistema operativo, esta pode não estar totalmente funcional.
+\nVerifique a compatibilidade e segurança da KeyStore com o fabricante do seu dispositivo e com o criador da ROM que está a utilizar.
+ Mudar automaticamente para o teclado anterior no ecrã de pesquisa
+ Permitir que aplicações de terceiros registem ou fotografem os ecrãs da aplicação
+ Entropia: %1$s bit
+ Entropia: alta
+ Entropia: calcular…
+ Considerar caracteres
+ Separador
+ Ignorar caracteres
+ MAIÚSCULAS
+ Capitalização de Título
+ Cores da entrada
+ Modo de captura de ecrã
+ Mostra as cores de fundo e de primeiro plano numa entrada
+ Ecrã de pesquisa
+ Manter o ecrã ligado ao ver uma entrada
+ Pelo menos um caractere de cada um
+ Excluir caracteres ambíguos
+ Número de caracteres: %1$d
+ Cor da base de dados
+ Cor do fundo da página inicial
+ Não permitiu que a aplicação usasse um alarme exato. Como resultado, as funcionalidades que requerem um temporizador não serão feitas com um tempo exato.
+ Permissão
+ Colorir caracteres da palavra-passe por tipo
+ Nome do ícone
+ Chave física
+ Selecione uma chave física.
+ XML malformado.
+ Cancelado pelo utilizador.
+ É necessário o driver para %1$s.
+ Unir dados
+ Lembrar chaves físicas
+ Modo de captura de ecrã
+ minúsculas
+ Cabeçalho de navegação
+ Gaveta de navegação aberta
+ Gaveta de navegação fechada
+ Herdar
+ Pesquisável
+ Preenchimento automático
+ Sequência de preenchimento automático
+ Dados personalizados
+ Grupo atual
+ Sensível a maiúsculas e minúsculas
+ Expressão regular
+ Unir de…
+ Guardar uma cópia em…
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index c595b9a1d..78b86eb87 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -24,14 +24,14 @@
Lembre-se de manter a sua aplicação atualizada, instalando novas versões.
Estamos a trabalhar para lançar esta funcionalidade o mais rápido possível.
Obrigado pela sua contribuição.
- está a incentivar os programadores a adicionar novas funcionalidades e a corrigir erros reportados pelos utilizadores.
- Ao contribuir,
+ está a incentivar os programadores a adicionar <strong>novas funcionalidades</strong> e a <strong>corrigir erros</strong> reportados pelos utilizadores.
+ Ao <strong>contribuir</strong>,
Ao comprar a versão <strong>pro</strong>,
- Esta funcionalidade está em desenvolvimento e necessita da sua contribuição para que esteja disponível brevemente.
- Para manter a nossa liberdade e estarmos sempre ativos, contamos com a sua contribuição.
+ Esta funcionalidade está <strong>em desenvolvimento</strong> e necessita da sua <strong>contribuição</strong> para que esteja disponível brevemente.
+ Para manter a nossa liberdade e estarmos sempre ativos, contamos com a sua <strong>contribuição.</strong>
Este <strong>estilo visual</strong> está disponível graças à sua generosidade.
- Ao comprar a versão pro, terá acesso a este estilo visual e ajudará especialmente a realizar projetos comunitários.
- Ao contrário de muitas aplicações de gestão de palavras-passe, esta aplicação não tem anúncios, é um programa de código-fonte livre e não recolhe dados pessoais para os servidores dela, seja qual for a versão que utiliza.
+ Ao comprar a versão pro, terá acesso a este <strong>estilo visual</strong> e ajudará especialmente a <strong>realizar projetos comunitários.</strong>
+ Ao contrário de muitas aplicações de gestão de palavras-passe, esta aplicação <strong>não tem anúncios</strong>, é <strong>um programa de código-fonte livre</strong> e não recolhe dados pessoais para os servidores dela, seja qual for a versão que utiliza.
Ajude a aumentar a estabilidade, segurança e novas funcionalidades.
Participar
Escolha como são ordenadas as entradas e os grupos.
@@ -128,7 +128,7 @@
As rodadas adicionais de encriptação fornecem mais proteção contra ataques de força bruta, mas podem tornar o processo de carregar e guardar mais lentos.
Rodadas de transformação
Para gerar a chave para o algoritmo de encriptação, a chave mestra é transformada usando uma função de derivação de chave com um salt aleatório.
- Algoritmo de encriptação usado para todos os dados.
+ Algoritmo de encriptação usado para todos os dados
Raiz
Dependendo do seu gestor de ficheiros, o KeePassDX pode não ter permissão de escrita no armazenamento.
Apenas leitura
@@ -209,8 +209,8 @@
Lembrar locais das bases de dados
Desbloqueio avançado
Não foi possível ler a base de dados.
- KeePassDX © %1$d Kunzisoft tem o código-fonte aberto e sem publicidade.
-\nÉ fornecido como está, sob a licença GPLv3, sem qualquer garantia.
+ KeePassDX © %1$d Kunzisoft tem o <strong>código-fonte aberto</strong> e <strong>sem publicidade</strong>.
+\nÉ fornecido como está, sob a <strong>licença GPLv3</strong>, sem qualquer garantia.
Informações sobre ficheiro
Informações sobre credenciais
Mudar automaticamente para o teclado anterior depois de executar a \"tecla automática\"
@@ -251,7 +251,7 @@
Sobre
Mascarar palavras-passe (***) por predefinição
Ocultar palavras-passe
- Para manter a liberdade, corrigir erros, adicionar funcionalidades e para sermos sempre ativos, contamos com sua contribuição.
+ Para <strong>manter a liberdade</strong>, <strong>corrigir erros</strong>, <strong>adicionar funcionalidades</strong> e <strong>para sermos sempre ativos</strong>, contamos com sua <strong>contribuição</strong>.
Página inicial
Comentários
Contribuição
@@ -395,7 +395,7 @@
Mostra nomes de utilizador nas listas de entradas
Mostrar nomes de utilizador
Não foi possível carregar a chave. Tente diminuir o \"Uso de memória\" do KDF.
- Não foi possível abrir a sua base de dados.
+ Não foi possível abrir a base de dados.
Editar entrada
Altere o modo de abertura para a sessão.
\n
@@ -623,4 +623,27 @@
Mudar automaticamente para o teclado anterior no ecrã de pesquisa
Pelo menos um caractere de cada um
Excluir caracteres ambíguos
+ Chave física
+ Selecione uma chave física.
+ XML malformado.
+ Não é possível fundir a partir de uma base de dados V1.
+ Cancelado pelo utilizador.
+ É necessário o driver para %1$s.
+ A localização da base de dados é desconhecida, a ação da base de dados não pode ser executada.
+ A chave física não é suportada.
+ Caixa de verificação da chave física
+ A chave não pode estar vazia.
+ Ficheiro corrompido.
+ Desafio já solicitado
+ Resposta já fornecida.
+ Não foi possível obter a resposta do desafio.
+ À espera do pedido de desafio…
+ À espera da resposta ao desafio…
+ Lembrar chaves físicas
+ Mantém um registo das chaves físicas utilizadas
+ Modo de captura de ecrã
+ Permitir que aplicações de terceiros registem ou fotografem os ecrãs da aplicação
+ Modo de captura de ecrã
+ <strong>Nenhum dado de utilizador é recuperado</strong>, esta aplicação não se liga a nenhum servidor, funciona apenas localmente e respeita plenamente a privacidade dos utilizadores.
+ Preenchimento automático
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 55304553d..3d430b7e2 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -148,7 +148,7 @@
Служба автозаполнения не может быть включена.
%1$s скопировано
Заполнение форм
- Алгоритм шифрования всех данных базы.
+ Алгоритм шифрования всех данных базы
При создании ключа для алгоритма шифрования, главный пароль преобразуется при помощи функции формирования ключа со случайной солью.
Использование памяти
Объём памяти, который будет использоваться функцией формирования ключа.
@@ -265,12 +265,12 @@
Выберите критерий сортировки записей и групп.
Участвуйте
Примите участие в проекте для повышения стабильности, безопасности и добавления новых возможностей.
- В отличие от многих других приложений управления паролями, здесь нет рекламы, и оно свободно от лицензирования. Приложение не собирает ваши личные данные на своих серверах независимо от версии, которую вы используете.
- Купив Pro–версию, у вас появится доступ к данным визуальным стилям, а также вы внесёте вклад в реализацию общественных проектов.
+ В отличие от многих других приложений управления паролями, здесь <strong>нет рекламы</strong>, и оно <strong>свободно от лицензирования</strong>. Приложение не собирает ваши личные данные на своих серверах независимо от версии, которую вы используете.
+ Купите Pro–версию и откройте доступ к этой <strong>теме</strong>. Покупая Pro-версию, вы помогаете <strong>разработчикам открытого ПО</strong>.
Эти <strong>визуальные стили</strong> доступны благодаря вашей щедрости.
- Для того, чтобы сохранить нашу независимость и быть всегда активными, мы рассчитываем на вашу поддержку.
+ Для развития нашего проекта и его независимости нам необходима ваша <strong>поддержка</strong>.
Эта функция находится <strong>в разработке</strong> и требует вашего <strong>участия</strong>, чтобы стать доступной в ближайшее время.
- Покупая Pro–версию,
+ Покупая <strong>Pro</strong>–версию,
<strong>Участвуя в проекте</strong>,
вы поощряете разработчиков добавлять <strong>новые возможности</strong> и <strong>исправлять ошибки</strong> в соответствии с вашими замечаниями.
Большое спасибо за поддержку.
@@ -409,11 +409,11 @@
Не показывать записи с истёкшим сроком действия
Контактная информация
Помощь проекту
- Для сохранения нашей независимости, исправления ошибок, добавления новых функций и поддержания разработки в активном состоянии, мы рассчитываем на вашу поддержку.
+ Для <strong>сохранения нашей независимости</strong>, <strong>исправления ошибок</strong>, <strong>добавления новых функций</strong> и <strong>поддержания разработки в активном состоянии</strong>, мы рассчитываем на вашу <strong>поддержку</strong>.
Быстрый поиск
Открывать поисковый запрос при открытии базы
Помнить расположение баз
- Отслеживать расположение баз
+ Помнить расположение файлов баз
Помнить расположение файлов ключей
Помнить расположение файлов ключей баз
Показывать последние базы
@@ -645,4 +645,27 @@
Исключить неоднозначные символы
Энтропия: вычисление…
ВЕРХНИЙ РЕГИСТР
+ Флажок аппаратного ключа
+ XML испорчен.
+ Для %1$s требуется драйвер.
+ Выберите аппаратный ключ.
+ Отменено пользователем.
+ Ответ уже предоставлен.
+ Вызов уже запрошен
+ Невозможно получить ответ на вызов.
+ Невозможно выполнить объединение из базы V1.
+ Аппаратный ключ не поддерживается.
+ Ключ не может быть пустым.
+ Файл повреждён.
+ Расположение базы неизвестно, действие с базой не может быть выполнено.
+ Помнить аппаратные ключи
+ Помнить используемые аппаратные ключи
+ Режим снимка экрана
+ Режим снимка экрана
+ Аппаратный ключ
+ Разрешать сторонним приложениям делать снимки или запись экрана приложения
+ Ожидание запроса на вызов…
+ Ожидание ответа на вызов…
+ <strong>Никакие пользовательские данные не собираются</strong>, это приложение не подключается ни к одному серверу, работает исключительно локально и полностью соблюдает конфиденциальность пользователей.
+ Автонабор
\ No newline at end of file
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
new file mode 100644
index 000000000..ab3cebcbd
--- /dev/null
+++ b/app/src/main/res/values-sq/strings.xml
@@ -0,0 +1,365 @@
+
+
+ Përpunoni zë
+ Bazë të dhënash
+ Shënime
+ Veti
+ Fjalëkalim
+ Rrënjë
+ Kujdes
+ Kyçe bazën e të dhënave
+ Ndryshoni kyçin e përgjithshëm
+ Grupi i tanishëm
+ Zë
+ Vetë-Shtype
+ Historik
+ Shtoni zë
+ Përpunojeni
+ Hiqe
+ Bashkëngjitje
+ Ruaje
+ Hape
+ Fshije
+ Skadon më
+ Shto grup
+ Aktivizoje
+ Çaktivizoje
+ Emër
+ Kërko
+ Emër përdoruesi
+ URL
+ Titull
+ Mbi
+ Mbylle bazën e të dhënave
+ Rregullime baze të dhënash
+ Rregullime
+ Pranoje
+ Ndjeshmëri ndaj shkrimi me të mëdha/të vogla
+ Të përgjithshme
+ Siguri
+ Kontribut
+ Përshtypje
+ Lejoje
+ Shtoni nyjë
+ Shtoni fushë
+ Gjedhe
+ Përdorur më
+ Krijuar më
+ UUID
+ Algoritëm
+ Version
+ Dhuroni
+ Ngjite
+ Raunde shndërrimi
+ Përdorim kujtese
+ Paralelizëm
+ Leje
+ Shtypni fjalëkalimin dhe mandej klikoni mbi këtë buton.
+ Biometrik
+ Fshi kyçe fshehtëzimi
+ Emër kartele
+ Njoftim
+ Ngjeshje
+ Asnjë
+ Përzgjedhje dorazi
+ Shumë faleminderit për kontributin tuaj.
+ Po punojmë fort për ta hedhur sa më shpejt në qarkullim këtë veçori.
+ Kontribuoni
+ Ngarko %1$s
+ Po gatitet…
+ Kontakt
+ Faqe hyrëse
+ Shtoni zë
+ Shto grup
+ Kyç i përgjithshëm
+ Fshehtëzim
+ Algoritëm fshehtëzimi
+ Mbarim kohe
+ Kohë plogështie para se të kyçet baza e të dhënave
+ Aplikacion
+ U spastrua e papastra
+ Gabim të papastre
+ S’u spastrua dot e papastra
+ Mbarim kohe për të papastrën
+ Sfond
+ Hape kartelën
+ Shtoni objekt
+ Hollësi kartele
+ Hollësi kredencialesh
+ Hollësi fjalëkalimi për një herë
+ Kutizë fjalëkalimi
+ Ikonë zëri
+ Ngjyrë baze të dhënash
+ Vleftësoje
+ Të hidhen tej ndryshimet\?
+ Hidhe tej
+ Prodhues fjalëkalimesh
+ Gjatësi fjalëkalimi
+ Numër fjalësh frazëkalimi
+ Shtoni bashkëngjitje
+ Hiqe fushën
+ Përditësim
+ Mbylli fushat
+ Po merret kyç baze të dhënash…
+ Po shfshehtëzohet bazë të dhënash…
+ Përdore si bazën parazgjedhje të të dhënave
+ Shifra
+ Anuloje
+ Ripohoni fjalëkalimin
+ Skaduar
+ Historik
+ Kartelë kyçi
+ Ndryshuar më
+ Trashëgoje
+ Sekuencë vetë-shtypjeje
+ S’u gjetën të dhëna zëri.
+ Fjalëkalim
+ Etiketa
+ Të dhënave vetjake
+ Titull
+ Ujdisni fjalëkalimin për një herë
+ Lloj OTP-je
+ E fshehtë
+ Periudhë (sekonda)
+ Numërator
+ Shifra
+ Shprehje e rregullt
+ Kartë Debiti / Krediti
+ Mbajtës
+ Numër
+ CVV
+ PIN
+ Kartë ID
+ Datë hedhjeje në qarkullim
+ Email
+ Adresë email
+ Wi-Fi
+ SSID
+ Kuletë kriptomonedhash
+ Token
+ Kyç publik
+ Kyç privat
+ Llogari
+ Bankë
+ Emër banke
+ SWIFT / BIC
+ IBAN
+ Shënim i Sigurt
+ Anëtarësi
+ Standard
+ Gjedhe
+ OTP
+ Emër përdoruesi
+ S’u krijua dot kartelë
+ S’u lexua dot baza e të dhënave.
+ E fshehtë OTP e pavlefshme.
+ Jepni një emër.
+ Përzgjidhni një kartelë kyçesh.
+ Përzgjidhni një kyç hardware.
+ S’u ngarkua dot baza e të dhënave.
+ Duhet ujdisur të paktën një kredencial.
+ Fjalëkalimet s’përputhen.
+ Ka tashmë një etiketë të tillë.
+ S’mund të lëvizni një grup për këtu.
+ S’mund të lëvizni një zë për këtu.
+ S’mund të kopjoni zë këtu.
+ S’mund të kopjoni grup këtu.
+ S’arrihet të krijohet kartelë baze të dhënash.
+ S’u ruajt dot baza e të dhënave.
+ E fshehta duhet të jetë nën formatin Base32.
+ Emri i kartelës ekziston tashmë.
+ S’arrihet të rikrijohet saktë lista.
+ Kartela që po provoni të dërgoni është shumë e madhe.
+ Të dhënat e kartelës ekzistojnë tashmë.
+ Ndodhi një gabim teksa hiqeshin të dhëna kartele.
+ Ndodhi një gabim teksa kryhej një veprim te baza e të dhënave.
+ Anuluar nga përdoruesi.
+ S’arrihet të përzihet që prej një baze të dhënash V1.
+ Emër fushe
+ Vlerë fushe
+ Kartelë e dëmtuar.
+ Përgjegjës kartelash
+ Prodho fjalëkalim
+ Ripohoni fjalëkalimin
+ Fjalëkalim i prodhuar
+ Emër grupi
+ Emër ikone
+ Kyçi s’mund të jetë i zbrazët.
+ Kartelë kyçi
+ Gjatësi
+ Fjalëkalim
+ Frazëkalim
+ Algoritëm i gabuar.
+ S’u kuptua dot formati i bazës së të dhënave.
+ Kartela e kyçit është e zbrazët.
+ Gjatësi
+ Fshihi fjalëkalimet
+ Si parazgjedhje, maskoji fjalëkalimet (***)
+ Ngjyrosi fjalëkalimet
+ Shfaq emra përdoruesi
+ Shfaq numër zërash
+ Shfaq Token OTP
+ Shfaq UUID
+ Shfaq numrin e zërave në një grup
+ Po krijohet bazë të dhënash…
+ Po ngarkohet baza e të dhënave…
+ Të vogla
+ Kopje e %1$s
+ Rregullime aplikacioni
+ Shkyçje e thelluar
+ Rregullime sigurie
+ Rregullime kyçi të përgjithshëm
+ Kopjoje
+ Anuloje
+ Fshihe fjalëkalimin
+ Kyçe bazën e të dhënave
+ Ruaji të dhënat
+ Ringarko të dhënat
+ Ruaj një kopje te …
+ Kërko
+ Shfaqe fjalëkalimin
+ Kalo te URL-ja
+ Zbraz koshin e riciklimeve
+ Rikthe historikun
+ Fshije historikun
+ Minus
+ Kurrë
+ S’ka përfundime kërkimi
+ Që të hapni këtë URL, instaloni një shfletues.
+ Hap bazë ekzistuese të dhënash
+ Krijoni bazë të re të dhënash
+ Kërkim i shpejtë
+ Po krijohet bazë e re të dhënash…
+ Po punohet…
+ Mbrojtje
+ Baza e të dhënave përmban UUID-ra të përsëdytur.
+ Mënyrë përzgjedhjeje
+ Mënyrë regjistrimi
+ Mbaj mend vendndodhje bazash të dhënash
+ Mbaj mend vendndodhje kartelash kyçesh
+ Shfaq kartelë të freskëta
+ Shfaq vendndodhje bazash të dhënash së fundi
+ Fshih lidhje të dëmtuara baze të dhënash
+ Importo veti aplikacioni
+ Përzgjidhni një kartelë që të importohen veti aplikacionesh
+ Po ruhet bazë të dhënash…
+ Mos e asgjëso aplikacionin…
+ Hapësirë
+ Renditi
+ Grupe përpara
+ Krijim
+ Ndryshim më
+ Hapur më
+ Special
+ Nënvijë
+ Version i pambuluar baze të dhënash.
+ Të mëdha
+ Të vazhdohet pa kyç fshehtëzimi\?
+ Të shtohet kartela, sido qoftë\?
+ Të hiqen këto të dhëna, sido qoftë\?
+ Version %1$s
+ Dukje
+ Kredenciale pajisjeje
+ Hyni me KeePassDX
+ Përzgjidhni zë…
+ Madhësi fjalëkalimi të prodhuar
+ Baza e të dhënave u hap
+ E papastër
+ Kyçe
+ Kyçje ekrani
+ Shfaq buton kyçjesh
+ Lëndë
+ Shkyçje e thelluar
+ Shteg
+ Caktoni një kyç të përgjithshëm
+ Të dhëna
+ Ngjeshje të dhënash
+ Përdorim koshi riciklimesh
+ Përdorim gjedhesh
+ Numër maksimum
+ Madhësi maksimum
+ Emër baze të dhënash
+ Përshkrim baze të dhënash
+ Emër parazgjedhje përdoruesi
+ Ngjyrë vetjake baze të dhënash
+ Version baze të dhënash
+ Tekst
+ Ndërfaqe
+ Tjetër
+ Gzip
+ Kosh riciklimesh
+ Gjedhe
+ Tastierë
+ Magikeyboard
+ Magikeyboard
+ Magikeyboard (KeePassDX)
+ Rregullime për Magikeyboard
+ Zë
+ Përzgjedhje zërash
+ Shfaq një njoftim, kur ka një zë
+ Mbarim kohe
+ %1$s
+ Dukje
+ Kyçe
+ Shtypje të dëgjueshme tastesh
+ Ndërroni tastierë
+ Skenë kredencialesh baze të dhënash
+ Fusha vetjake
+ Përzgjidhni zë
+ Listë bllokimi aplikacionesh
+ Mos lejo kyç të përgjithshëm
+ Fshije fjalëkalimin
+ Vetëruaj bazë të dhënash
+ Krijoni kartelën e bazës tuaj të të dhënave
+ Hapni një bazë ekzistuese të dhënash
+ Shtoni objekte te baza juaj e të dhënave
+ Kërkoni nëpër zëra
+ Përpunoni zërin
+ Krijoni një fjalëkalim të fuqishëm
+ Shtoni fusha vetjake
+ Shtoni bashkëngjitje
+ Shkyçni bazën tuaj të të dhënave
+ Aplikoni mbrojtje nga shkrimi për bazën tuaj të të dhënave
+ Kopjoni një fushë
+ Kyçe bazën e të dhënave
+ Merrni pjesë
+ Në ecuri e sipër: %1$d %%
+ Po përfundohet…
+ B
+ KiB
+ MiB
+ GiB
+ Të paktën një shenjë nga secili
+ Përjashto shenja të dykuptimta
+ Ndarës
+ të vogla
+ TË MËDHA
+ Numër shenjash: %1$d
+ Temë aplikacioni
+ Temë e përdorur te aplikacioni
+ Ndriçim teme
+ Standarde
+ Vetjake
+ Paketë ikonash
+ Ngjyra zërash
+ Fshihi zërat e skaduar
+ XML e keqformuar.
+ Gabim shkyçjeje të thelluar: %1$s
+ Blloko vetëplotësim
+ Lejon prekjen e butoni “Hape”, nëse s’janë përzgjedhur kredenciale
+ Sendërtim për Android i përgjegjësit KeePass të fjalëkalimeve
+ Kërko një kërkim, kur hapet një bazë të dhënash
+ Ngarkimi i kësaj kartele do të zëvendësojë atë ekzistuesen.
+ Vetëplotësoje
+ Prekeni që të fshihen kyçe shkyçjeje të thelluar
+ Shkyçje biometrike
+ Ju lejon të skanoni elementë biometrikë për të hapur bazën e të dhënave
+ Fshi krejt kyçet e fshehtëzimit të lidhur me njohje shkyçjesh të thelluaara
+ Të fshihen krejt kyçet e fshehtëzimit të lidhur me njohje shkyçjesh të thelluara\?
+ Mbrapsht te tastiera e mëparshme
+ Tasti “Backspace”
+ Pyet të ruhen të dhënat
+ Pyet për ruajtje të dhënash, kur plotësohet mbushja e një formulari
+ Listë bllokimesh që pengon vetëplotësim nga aplikacione
+ Listë bllokimesh përkatësish Web
+
\ No newline at end of file
diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml
new file mode 100644
index 000000000..f7967544f
--- /dev/null
+++ b/app/src/main/res/values-th/strings.xml
@@ -0,0 +1,89 @@
+
+
+ อัลกอริทึมของการเข้ารหัส
+ การเข้ารหัส
+ ร่วมแก้ไข
+ ข้อเสนอแนะ
+ แอปพลิเคชั่นจัดการรหัสผ่านแบบ KeePass บน Android
+ ยอมรับ
+ เพื่มกลุ่ม
+ รหัสผ่านหลัก
+ แอป
+ วงเล็บ
+ อนุญาต
+ ล้างคลิปบอร์ดแล้ว
+ คลิปบอร์ดผิดพลาด
+ ยืนยันรหัสผ่าน
+ รหัสผ่าน
+ ตั้งค่ารหัสผ่านแบบใช้ครั้งเดียว
+ รหัสผ่านไม่ตรงกัน
+ การตั้งค่าแอป
+ หน้าหลัก
+ ความปลอดภัย
+ ปิดบังรหัสผ่านเป็น (***) โดยค่าเรื่มต้น
+ ระยะเวลาที่ไม่ได้ใช้งานก่อนที่ฐานข้อมูลจะถูกล็อก
+ ใช้งานการยืนยันข้้นสูงเพื่อปลดล็อกฐานข้อมูล
+ การตั้งค่า
+ รหัสผ่าน
+ สร้างรหัสผ่าน
+ เปลี่ยนรหัสผ่านหลัก
+ การตั้งค่ารหัสผ่านหลัก
+ กำหนดรหัสผ่านหลัก
+ ความยาวของรหัสผ่าน
+ ต้องเลือกรูปแบบของรหัสผ่านที่จะสร้างอย่างน้อยหนึ่งประเภท
+ สร้างรหัสผ่าน
+ รหัสผ่าน
+ ซ่อนรหัสผ่าน
+ ยืนยันรหัสผ่าน
+ ติดต่อ
+ เปิดฐานข้อมูลที่มีอยู่แล้ว
+ การเข้าถึงไฟล์ถูกเพิกถอนโดยตัวจัดการไฟล์
+ การเข้าถึงไฟล์ถูกเพิกถอนโดยตัวจัดการไฟล์ ปิดฐานข้อมูลและเปิดใหม่จากตำแหน่งดังกล่าวอีกครั้ง
+ เปิดไฟล์ฐานข้อมูลที่มีอยู่แล้ว
+ สรัางไฟล์ฐานข้อมูลของคุณ
+ สร้างไฟล์ฐานข้อมูลรหัสผ่านแรกของคุณ
+ ป้อนรหัสผ่าน และ/หรือ Keyfile เพื่อปลดล็อกฐานข้อมูลของคุณ
+\n
+\nสำรองไฟล์ฐานข้อมูลของคุณในที่ปลอดภัยหลังจากการเปลี่ยนแปลงแต่ละครั้ง
+ ปลดล็อกฐานข้อมูล
+ ช่องทำเครื่องหมาย Keyfile
+ ปุ่มสลับการมองเห็นรหัสผ่าน
+ จำนวนคำวลีรหัสผ่าน
+ เพิ่มโหนด
+ สีพื้นหลังของรายการ
+ เพิ่มรายการ
+ แก้ไขรายการ
+ อนุพันธ์ของฟังก์ชันหลัก
+ หมดเวลา
+ Extended ASCII
+ ในบางอุปกรณ์ ระบบไม่อนุญาตให้แอปใช้คลิปบอร์ด
+ ไม่สามารถล้างคลิปบอร์ด
+ ระยะเวลาของคลิปบอร์ด
+ ระยะเวลาของข้อมูลในคลิปบอร์ด (หากอุปกรณ์รองรับ)
+ พื้นหลัง
+ เปิดไฟล์
+ เพิ่มรายการ
+ เพิ่มกลุ่ม
+ เพื่มรายการ
+ ข้อมูลไฟล์
+ ข้อมูลประจำตัว
+ ข้อมูลรหัสผ่านแบบใช้ครั้งเดียว
+ ช่องทำเครื่องหมายรหัสผ่าน
+ ช่องทำเครื่องหมายคีย์ฮาร์ดแวร์
+ ไอคอนรายการ
+ สีของฐานข้อมูล
+ สีพื้นหน้ารายการ
+ ส่วนหัวของการนำทาง
+ ลิ้นชักการนำทางเปิดอยู่
+ ปิดลิ้นชักการนำทาง
+ ตรวจสอบ
+ ละทิ้งการเปลี่ยนแปลง\?
+ ละทิ้ง
+ ตัวสร้างรหัสผ่าน
+ เพิ่มช่อง
+ เพิ่มไฟล์แนบ
+ ลบฟิลด์
+ อัปเดต
+ ลบ
+ ปิดฟิลด์
+
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index dd553041c..03079a4db 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -70,7 +70,7 @@
Bu URI, KeePassDX\'te işlenemedi.
Bir anahtar dosyası seçin.
Tüm veri tabanınızı yükleyecek kadar bellek yok.
- Veri tabanınız yüklenemedi.
+ Veri tabanı yüklenemedi.
Anahtar yüklenemedi. KDF \"Bellek Kullanımı\" nı azaltmaya çalışın.
En az bir parola oluşturma türü seçilmelidir.
Parolalar uyuşmuyor.
@@ -137,7 +137,7 @@
Dosya yöneticinize bağlı olarak KeePassDX\'in depolama alanınıza yazmasına izin verilmeyebilir.
Kaldır
Kök
- Tüm veriler için kullanılan veri tabanı şifreleme algoritması.
+ Tüm veriler için kullanılan veri tabanı şifreleme algoritması
Şifreleme algoritmasının anahtarını üretmek için ana anahtar, rastgele anahtar türetme işlevi kullanılarak dönüştürülür.
Dönüşüm turları
Ek şifreleme turları, kaba kuvvet saldırılarına karşı daha yüksek koruma sağlar, ancak yükleme ve kaydetmeyi gerçekten yavaşlatabilir.
@@ -279,7 +279,7 @@
Katılın
Daha fazla özellik ekleyerek istikrarı, güvenliği artırmaya yardımcı olun.
Birçok parola yönetimi uygulamasının aksine, bu uygulama <strong>reklam içermez</strong>, <strong> copyleft lisanslı özgür yazılımdır</strong> ve hangi sürümü kullanırsanız kullanın, sunucularında kişisel veri toplamaz.
- Pro sürümü satın alarak, bu görsel stile erişebilecek ve özellikle topluluk projelerinin gerçekleştirilmesine yardımcı olacaksınız.
+ Pro sürümü satın alarak, bu <strong>görsel stile</strong> erişebilecek ve özellikle <strong>topluluk projelerinin gerçekleştirilmesine yardımcı olacaksınız.</strong>
Bu <strong>görsel stil</strong>, cömertliğiniz sayesinde kullanılabilir.
Özgürlüğümüzü korumak ve daima aktif olmak için <strong>katkılarınıza</strong> güveniyoruz
Bu özellik <strong>geliştirme aşamasındadır</strong> ve <strong>katkılarınızın</strong> yakında kullanıma sunulmasını gerektirir.
@@ -601,7 +601,7 @@
Android, verilerini anında değiştirebildiğinden dosyanın sağlama toplamı garanti edilmez. Doğru bütünlük için dosya uzantısını .bin olarak değiştirin.
Ekranı açık tut
Girdi renkleri
- Bir girdide ön plan ve arka plan renklerini görüntüler
+ Bir girdi için ön plan ve arka plan renklerini görüntüler
Girdiyi izlerken ekranı açık tutun
Gezinme başlığı
Gezinme çekmecesi açık
@@ -640,4 +640,27 @@
Ayırıcı
BÜYÜK HARF
İlk Harfleri Büyük
+ <strong>Hiçbir kullanıcı verisi alınmaz</strong>, bu uygulama herhangi bir sunucuya bağlanmaz, yalnızca yerel olarak çalışır ve kullanıcıların gizliliğine tamamen saygı duyar.
+ Anahtar boş olamaz.
+ Donanım anahtarlarını hatırla
+ V1 veri tabanından birleştirme yapılamıyor.
+ Veri tabanı konumu bilinmiyor, veri tabanı eylemi gerçekleştirilemiyor.
+ Donanım anahtarı desteklenmiyor.
+ Bozuk dosya.
+ Kullanılan donanım anahtarlarının kaydını tutar
+ Donanım anahtarı onay kutusu
+ Doğrulama isteği bekleniyor…
+ Doğrulama yanıtı bekleniyor…
+ Donanım anahtarı
+ Otomatik Yaz
+ Bir donanım anahtarı seçin.
+ XML hatalı biçimlendirildi.
+ Doğrulama zaten istendi
+ Yanıt zaten verildi.
+ Doğrulamadan yanıt alınamıyor.
+ Kullanıcı tarafından iptal edildi.
+ %1$s için sürücü gerekli.
+ Ekran görüntüsü modu
+ Üçüncü taraf uygulamaların uygulamanın ekran görüntülerini kaydetmesine veya almasına izin verin
+ Ekran görüntüsü modu
\ No newline at end of file
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index f6647180c..06cf11933 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -432,7 +432,7 @@
Ступінь паралелізму (тобто кількість потоків), необхідних для функції створення ключа.
Паралельність
Використання пам\'яті
- Алгоритм шифрування бази даних, застосований для всіх даних.
+ Алгоритм шифрування бази даних, застосований для всіх даних
Перезапустіть застосунок, який містить форму, для застосування блокування.
Блокування автозаповнення
Вебдомени для яких вимкнено автозаповнення
@@ -645,4 +645,27 @@
Регістр Заголовка
Роздільник
ВЕРХНІЙ РЕГІСТР
+ Прапорець апаратного ключа
+ Апаратний ключ
+ Хибний XML.
+ Відповідь уже надана.
+ Вимагається драйвер для %1$s.
+ Не вдалося об\'єднати з базою даних V1.
+ Пам\'ятати апаратні ключі
+ Пам\'ятати використовувані апаратні ключі
+ Режим знімка екрана
+ Дозволити стороннім застосункам записувати або робити знімки екрана застосунку
+ Режим знімка екрана
+ Очікування запиту на виклик…
+ Апаратний ключ не підтримується.
+ Очікування відповіді на виклик…
+ Виберіть апаратний ключ.
+ Виклик уже запитаний
+ Не вдалося отримати відповідь на виклик.
+ Скасовано користувачем.
+ Розташування бази даних невідоме, дія бази даних не може бути виконана.
+ Ключ не може бути порожнім.
+ Пошкоджений файл.
+ <strong>Жодні користувацькі дані не збираються</strong>, цей застосунок не з\'єднується з жодним сервером, він працює лише локально та повністю поважає приватність користувачів.
+ Автовведення
\ No newline at end of file
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 26de2983b..50a388a9b 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -34,9 +34,9 @@
Ghi chú
Huỷ bỏ
Đã truy cập
- Để có thể duy trì sự tự do, sửa lỗi, thêm tính năng và luôn hoạt động, chúng tôi dựa vào sự đóng góp của bạn.
- KeePassDX © %1$d Kunzisoft là mã nguồn mở và không có quảng cáo.
-\nĐược cung cấp nguyên trạng, với điều khoản của giấy phép GPLv3, không có bất cứ bảo đảm nào.
+ Để có thể <strong>duy trì sự tự do</strong>, <strong>sửa lỗi</strong>, <strong>thêm tính năng</strong> và <strong>luôn hoạt động</strong>, chúng tôi dựa vào <strong>sự đóng góp</strong> của bạn.
+ KeePassDX © %1$d Kunzisoft là <strong>mã nguồn mở</strong> và <strong>không có quảng cáo</strong>.
+\nĐược cung cấp nguyên trạng, với điều khoản của giấy phép <strong>GPLv3</strong>, không có bất cứ bảo đảm nào.
Chữ số
Dùng làm cơ sở dữ liệu mặc định
Đang giải mã nội dung cơ sở dữ liệu…
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index f064f732f..309cb246f 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -39,8 +39,8 @@
正在解密数据库内容…
设为默认数据库
数字
- KeePassDX © %1$d 是 Kunzisoft 旗下的一个不含广告的开源软件。
-\n它是根据GPLv3许可证分发的,您可在遵循GPLv3或者更高版本的协议下重新发布。Kunzisoft对软件的质量和性能等问题不提供任何形式的担保。
+ KeePassDX © %1$d 是 Kunzisoft 旗下的一个<strong>不含广告</strong>的<strong>开源软件</strong>。
+\n它是根据<strong>GPLv3</strong>许可证分发的,您可在遵循GPLv3或者更高版本的协议下重新发布。Kunzisoft对软件的质量和性能等问题不提供任何形式的担保。
打开已有数据库
访问时间
取消
@@ -56,7 +56,7 @@
网址
用户名
不支持Arcfour流式加密。
- 无法在KeePassDX中处理此URI。
+ 无法在 KeePassDX 中处理此 URI 。
无法新建文件
无法读取数据库。
请确保路径正确。
@@ -135,7 +135,7 @@
编辑条目
密钥推导函数
找不到条目数据。
- 无法加载您的数据库。
+ 无法加载数据库。
无法加载密钥。尝试降低KDF的“内存使用”值。
无法启用自动填充服务。
找不到文件。请重新打开文件。
@@ -156,7 +156,7 @@
根据您的文件管理器,KeePassDX 可能不允许在您的存储中写入数据。
显示最近打开的文件
显示最近打开数据库的位置
- 加密数据库所有数据时采用的算法。
+ 用于所有数据的数据库加密算法
将迭代主密钥以生成加密数据库所需的密钥,转换方式为随机加盐算法。
内存使用量
密钥推导算法使用的内存。
@@ -292,7 +292,7 @@
选择条目和群组的排序方式。
参与开发
帮助增加稳定性,安全性并添加更多的功能。
- 不同于大多数的密码管理应用,无论您是使用免费版本还是付费版本的 KeePassDX,这都是一款没有广告,基于 copylefted 版权协议的自由软件。同时,本软件的任何版本都不会收集您的任何个人信息。
+ 不同于大多数的密码管理应用,无论您是使用免费版本还是付费版本的 KeePassDX,这都是一款<strong>没有广告</strong>,<strong>基于 copylefted 版权协议的自由软件</strong>。同时,本软件的任何版本都不会收集您的任何个人信息。
通过购买高级版本,您将解锁全部<strong>主题样式</strong>,重要的是,您会为<strong>社区项目的进行</strong>提供帮助
此<strong>主题样式</strong>已可用,感谢您的慷慨相助。
为继续建设此自由项目让其保持活跃,我们需要您的<strong>捐赠。</strong>
@@ -311,7 +311,7 @@
剪贴板持续时间过期或通知在您开始使用后关闭时,锁定数据库
回收站
条目选择
- 在KeePass DX中查看条目时,用该条目填充 Magikeyboard
+ 在 KeePass DX 中查看条目时,用该条目填充 Magikeyboard
删除密码
在尝试连接数据库后删除输入的密码
打开文件
@@ -646,4 +646,26 @@
按类型给密码字符加上颜色
熵:高
熵:%1$s bit
+ 硬件秘钥复选框
+ 等待挑战请求中…
+ 等待挑战响应中…
+ 硬件秘钥
+ 选择硬件秘钥。
+ XML 文件畸形。
+ 已请求挑战
+ 已提供响应。
+ 无法从挑战获取响应。
+ 被用户取消。
+ 无法从 v1 版数据库进行合并。
+ 数据库位置未知,无法进行数据库操作。
+ 硬件秘钥不受支持。
+ %1$s 的驱动是必需的。
+ 秘钥不能为空。
+ 文件损坏。
+ 记住硬件秘钥
+ 保留所用硬件秘钥的痕迹
+ 截屏模式
+ 允许第三方应用对 KeePassDX 屏幕进行录制或截图
+ 截屏模式
+ <strong>不获取用户数据</strong>,此应用不连接任何服务器,仅在本地运行,并充分尊重用户的隐私。
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 1ca58b630..2944dfcc3 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -622,4 +622,30 @@
\n
\n基於操作系統的原始 API 實現,Keystore 有可能無法發揮其應有的作用。
\n請向設備的製造商和 ROM 的創建者咨詢 KeyStore 的兼容性和安全性。
+ 密碼短句字數
+ 密碼短句
+ 以顏色標示密碼
+ 強度:%1$s 位元
+ 強度:高
+ 強度:計算中…
+ 小寫
+ 排除容易混淆的字元
+ 硬件密鑰方塊
+ 硬件密鑰
+ 已提供回應。
+ 已被用戶取消。
+ 資料庫位置不明,無法操作資料庫。
+ 不支援硬件密鑰。
+ 密鑰不可留空。
+ 已損毀的檔案。
+ 記住硬件密鑰
+ 搜尋介面
+ 在搜尋介面自動切換到之前的鍵盤
+ 大寫
+ 螢幕截圖模式
+ 選擇硬件密鑰。
+ 允許第三方應用程式對本應用程式錄影或截圖
+ 字元數: %1$d
+ 分隔符號
+ 螢幕截圖模式
\ No newline at end of file
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index a8fd6743e..1ee2f2d1e 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -30,8 +30,12 @@
DX
Pro
- https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro
- https://www.keepassdx.com/contribution
+ https://play.google.com/store/apps/details?id=%1$s
+ https://f-droid.org/en/packages/%1$s
+ com.kunzisoft.keepass.pro
+ com.kunzisoft.hardware.key
+ https://gitlab.com/kunzisoft/android-hardware-key-driver/-/releases/
+ https://www.keepassdx.com/#donation
https://www.keepassdx.com
https://www.keepassdx.com/#icons
https://github.com/Kunzisoft/KeePassDX/issues
@@ -70,6 +74,8 @@
true
enable_keep_screen_on_key
true
+ enable_screenshot_mode_key
+ false
auto_focus_search_key
false
subdomain_search_key
@@ -91,6 +97,8 @@
true
remember_keyfile_locations_key
true
+ remember_hardware_key_key
+ true
advanced_unlock_explanation_key
biometric_unlock_enable_key
false
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 556135f33..c5f01429e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -56,6 +56,7 @@
One-time password info
Password checkbox
Keyfile checkbox
+ Hardware key checkbox
Repeat toggle password visibility
Entry icon
Database color
@@ -78,12 +79,16 @@
Close fields
Select to copy %1$s to clipboard
Retrieving database key…
+ Waiting for the challenge request…
+ Waiting for the challenge response…
Database
Templates
Decrypting database content…
Use as default database
Digits
- KeePassDX © %1$d Kunzisoft is <strong>open source</strong> and <strong>without advertising</strong>. \nIt is provided as is, under <strong>GPLv3</strong> license, without any warranty.
+ KeePassDX © %1$d Kunzisoft is <strong>open source</strong> and <strong>without advertising</strong>.
+\nIt is provided as is, under <strong>GPLv3</strong> license, without any warranty.
+ <strong>No user data is retrieved</strong>, this application does not connect to any server, works only locally and fully respects the privacy of users.
In order to <strong>keep our freedom</strong>, <strong>fix bugs</strong>, <strong>add features</strong> and <strong>to be always active</strong>, we count on your <strong>contribution</strong>.
Accessed
Cancel
@@ -96,6 +101,7 @@
History
Attachments
Keyfile
+ Hardware key
Modified
Searchable
Inherit
@@ -159,8 +165,10 @@
Enter a name.
This word is reserved and cannot be used.
Select a keyfile.
+ Select a hardware key.
No memory to load your entire database.
- Could not load your database.
+ XML malformed.
+ Could not load the database.
Could not load the key. Try to lower the KDF \"Memory Usage\".
At least one password generation type must be selected.
At least one credential must be set.
@@ -176,7 +184,7 @@
You cannot copy a group here.
Unable to create database file.
Unable to create database with this password and keyfile.
- Could not save database.
+ Could not save the database.
Secret key must be in Base32 format.
Counter must be between %1$d and %2$d.
Period must be between %1$d and %2$d seconds.
@@ -192,9 +200,19 @@
The file data already exists.
An error occurred while removing the file data.
An error occurred while performing an action on the database.
+ "Challenge already requested"
+ Response already provided.
+ Unable to get the response from the challenge.
+ Cancelled by user.
+ Driver for %1$s is required.
+ Unable to merge from a database V1.
+ Database location is unknown, database action cannot be performed.
+ Hardware key is not supported.
+ "Key cannot be empty."
Field name
Field value
Could not find file. Try reopening it from your file browser.
+ Corrupted file.
File manager
Generate password
Confirm password
@@ -289,6 +307,8 @@
Keeps track of where databases are stored
Remember keyfile locations
Keeps track of where keyfiles are stored
+ Remember hardware keys
+ Keeps track of the hardware keys used
Show recent files
Show locations of recent databases
Hide broken database links
@@ -303,7 +323,7 @@
App properties exported
Error during app properties exportation
Root
- Database encryption algorithm used for all data.
+ Database encryption algorithm used for all data
To generate the key for the encryption algorithm, the master key is transformed using a randomly salted key derivation function.
Transformation rounds
Additional encryption rounds provide higher protection against brute force attacks, but can really slow down loading and saving.
@@ -353,6 +373,7 @@
Access to the file revoked by the file manager, close the database and reopen it from its location.
You have not allowed the app to use an exact alarm. As a result, the features requiring a timer will not be done with an exact time.
The hash of the file is not guaranteed because Android can change its data on the fly. Change the file extension to .bin for correct integrity.
+ Merge successfully completed
Permission
Version %1$s
Build %1$s
@@ -541,6 +562,8 @@
Save the database after every important action (in \"Modifiable\" mode)
Keep screen on
Keep the screen on when watching the entry
+ Screenshot mode
+ Allow third party apps to record or take screenshots of the app
Educational hints
Highlight elements to learn how the app works
Reset educational hints
@@ -617,6 +640,7 @@
UPPER CASE
Title Case
Character count: %1$d
+ Screenshot mode
- @string/lower_case
- @string/upper_case
@@ -676,7 +700,7 @@
Icon pack
Icon pack used in the app
Entry colors
- Displays foreground and background colors in an entry
+ Displays foreground and background colors for an entry
Hide expired entries
Expired entries are not shown
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 8e0ab3909..7c3df40ed 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -565,7 +565,7 @@
-