Compare commits

...

72 Commits

Author SHA1 Message Date
J-Jamet
123288e745 Merge branch 'release/2.9.17' 2021-04-13 09:11:05 +02:00
J-Jamet
11aae77caf Update CHANGELOG 2021-04-10 20:56:25 +02:00
J-Jamet
8212cede6e Merge branch 'feature/Duration_Preference' into develop #579 2021-04-10 20:53:35 +02:00
J-Jamet
a3c51884f4 Fix timeout strings 2021-04-10 20:53:24 +02:00
J-Jamet
b8890aca7f Better notification timer implementation 2021-04-10 20:30:48 +02:00
J-Jamet
014b0cce14 Add duration preference with number picker 2021-04-10 19:29:19 +02:00
J-Jamet
c40b255022 Check properties 2021-04-08 16:40:01 +02:00
J-Jamet
1742d265f3 Better stylish implementation 2021-04-08 16:26:14 +02:00
J-Jamet
ff185f6505 Upgrade version and CHANGELOG 2021-04-08 12:58:39 +02:00
J-Jamet
346b517c9d Force twofish padding compatibility #955 2021-04-08 12:55:23 +02:00
J-Jamet
949905f6e2 Merge branch 'feature/Import_Export_App_Properties' into develop 2021-04-08 12:06:52 +02:00
J-Jamet
b9e26fecfd Export and import properties 2021-04-08 11:45:23 +02:00
J-Jamet
de69a78a98 First commit to import and export app properties 2021-04-07 16:13:40 +02:00
J-Jamet
1c341c34a3 Merge tag '2.9.16' into develop
2.9.16
2021-04-07 09:53:13 +02:00
J-Jamet
33beb57e9d Merge branch 'release/2.9.16' 2021-04-07 09:53:07 +02:00
J-Jamet
66eeadca0b Upgrade version code 2021-04-06 17:39:27 +02:00
J-Jamet
a10d1c98a8 Fix KDB parcelable 2021-04-06 17:32:40 +02:00
J-Jamet
59ead4986f Move Parent Parcelable 2021-04-06 15:05:11 +02:00
J-Jamet
09f6c18189 Small changes 2021-04-06 15:02:24 +02:00
J-Jamet
0f3ad7c8b1 Fix select custom icon 2021-04-05 19:06:54 +02:00
J-Jamet
0487dea7fc Fix null cache directory 2021-04-05 11:32:07 +02:00
J-Jamet
8cac1ee284 Merge branch 'feature/ExternalFileHelper' into develop 2021-04-05 00:06:03 +02:00
J-Jamet
196620e1bd Remove unused methods 2021-04-05 00:04:25 +02:00
J-Jamet
43d6c76873 Refactor open document click listener 2021-04-04 23:44:25 +02:00
J-Jamet
b864c39a0d Fix add database workflow in some devices 2021-04-04 23:13:24 +02:00
J-Jamet
818b975111 Change default type verification to create document 2021-04-04 09:56:09 +02:00
J-Jamet
d5fbc8393f Change file creation methods 2021-04-03 19:40:55 +02:00
J-Jamet
7b5e9d2344 Better parcelable entry CREATOR implementation #948 2021-04-02 09:37:18 +02:00
J-Jamet
7fc2d95886 Fix document file retrievment 2021-04-01 10:52:00 +02:00
J-Jamet
78d3b369bb Move Parcelable inheritance 2021-03-31 19:39:07 +02:00
J-Jamet
bb3620680b Upgrade version 2021-03-31 17:58:49 +02:00
J-Jamet
d4a45655ca Merge tag '2.9.15' into develop
2.9.15
2021-03-29 22:01:52 +02:00
J-Jamet
c9c739fd52 Merge branch 'release/2.9.15' 2021-03-29 22:01:44 +02:00
J-Jamet
2b359cc592 Remove unused code 2021-03-29 21:00:10 +02:00
J-Jamet
151b7a323d Upgrade version code 2021-03-29 13:58:30 +02:00
J-Jamet
1063dc2b63 Add TODO SparseArray 2021-03-29 13:57:30 +02:00
J-Jamet
f9f59a6eb1 Replace serializable UUID by Parcelable UUID 2021-03-29 13:49:49 +02:00
J-Jamet
73156cc337 Fix clipboard null exception 2021-03-29 13:09:22 +02:00
J-Jamet
7d53607f49 Capture exception when launching cipher action 2021-03-29 13:07:55 +02:00
J-Jamet
7539945465 Capture exception when error when launching database action 2021-03-29 13:01:31 +02:00
J-Jamet
51df8e7bb1 Try to fix rare bug 2021-03-29 12:52:12 +02:00
J-Jamet
17029ce67c Fix bad padding exception 2021-03-29 12:42:53 +02:00
J-Jamet
8cedc313cf Upgrade version code 2021-03-27 10:54:26 +01:00
J-Jamet
5afe3acac1 Remove unused string 2021-03-27 10:23:38 +01:00
J-Jamet
9887b58b71 Merge branch 'develop' of https://hosted.weblate.org/git/keepass-dx/strings into translations 2021-03-27 10:18:55 +01:00
J-Jamet
ec8363ba6a Merge branch 'develop' into release/2.9.15 2021-03-27 10:13:53 +01:00
J-Jamet
fcfb71f13b Fix disable Memory Usage setting with AES #941 2021-03-27 10:12:29 +01:00
J-Jamet
3a12e431ff Update CHANGELOG 2021-03-27 05:49:29 +01:00
J-Jamet
bc4ed8e123 Update CHANGELOG 2021-03-27 05:44:09 +01:00
J-Jamet
445e9540a5 Merge branch 'feature/Dynamic_Memory_And_Encrypt_Module' into develop 2021-03-26 20:06:12 +01:00
Joan Jaume Oliver
bbc2a2a9dd Translated using Weblate (Spanish)
Currently translated at 98.6% (515 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/es/
2021-03-26 19:29:42 +01:00
Joan Jaume Oliver
5117bc78b6 Translated using Weblate (Catalan)
Currently translated at 48.2% (252 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ca/
2021-03-26 19:29:41 +01:00
Reza Almanda
26d8b2fa22 Translated using Weblate (Indonesian)
Currently translated at 74.3% (388 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/id/
2021-03-25 01:29:42 +01:00
random r
2b7fe35305 Translated using Weblate (Italian)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2021-03-23 21:30:04 +01:00
Vít Šindlář
d5819ea4d0 Translated using Weblate (Czech)
Currently translated at 99.6% (520 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/cs/
2021-03-23 21:30:04 +01:00
Hosted Weblate
0cf136712a Merge branch 'origin/develop' into Weblate. 2021-03-20 14:07:16 +01:00
Milo Ivir
34a453873a Translated using Weblate (Croatian)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/hr/
2021-03-20 14:07:16 +01:00
Kunzisoft
0acac3b096 Translated using Weblate (French)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/fr/
2021-03-20 14:07:04 +01:00
Oymate
ede6070e43 Translated using Weblate (Bengali (Bangladesh))
Currently translated at 4.2% (22 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/bn_BD/
2021-03-17 08:18:07 +01:00
jan madsen
a61b1d4337 Translated using Weblate (Danish)
Currently translated at 93.8% (490 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/da/
2021-03-17 08:18:06 +01:00
WaldiS
0f8c71a9df Translated using Weblate (Polish)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2021-03-14 13:02:53 +01:00
gnu-ewm
cb0b6e010d Translated using Weblate (Polish)
Currently translated at 99.8% (521 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/pl/
2021-03-12 21:02:54 +01:00
Hisikawa Mizuki
23dc7be1ab Translated using Weblate (Japanese)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/
2021-03-12 21:02:54 +01:00
Oliver Cervera
4b14ad07d2 Translated using Weblate (Italian)
Currently translated at 99.8% (521 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/it/
2021-03-12 21:02:54 +01:00
Oğuz Ersen
8a4bf7896f Translated using Weblate (Turkish)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/tr/
2021-03-11 17:14:10 +01:00
Allan Nordhøy
208ea29643 Translated using Weblate (Norwegian Bokmål)
Currently translated at 76.6% (400 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nb_NO/
2021-03-11 17:14:09 +01:00
Eric
7c52ec731a Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/zh_Hans/
2021-03-11 17:14:09 +01:00
Ihor Hordiichuk
c6ee38e435 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/uk/
2021-03-11 17:14:08 +01:00
solokot
65253cc5b9 Translated using Weblate (Russian)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2021-03-11 17:14:08 +01:00
Stephan Paternotte
d1a1a23cbc Translated using Weblate (Dutch)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/nl/
2021-03-11 17:14:08 +01:00
HARADA Hiroyuki
e8bb3a5ba7 Translated using Weblate (Japanese)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ja/
2021-03-11 17:14:07 +01:00
Retrial
22b8f82770 Translated using Weblate (Greek)
Currently translated at 100.0% (522 of 522 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/el/
2021-03-11 17:14:07 +01:00
93 changed files with 1544 additions and 1010 deletions

View File

@@ -1,6 +1,16 @@
KeePassDX(2.9.17)
* Import / Export app properties #839
* Force twofish padding compatibility #955
* Better timeout preference #579
KeePassDX(2.9.16)
* Fix small bugs #948
KeePassDX(2.9.15) KeePassDX(2.9.15)
* Fix themes #935 * Fix themes #935 #926
* Decrease default clipboard time #934 * Decrease default clipboard time #934
* Better opening performance #929 #933
* Fix memory usage setting #941
KeePassDX(2.9.14) KeePassDX(2.9.14)
* Add custom icons #96 * Add custom icons #96

View File

@@ -11,8 +11,8 @@ android {
applicationId "com.kunzisoft.keepass" applicationId "com.kunzisoft.keepass"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 30 targetSdkVersion 30
versionCode = 66 versionCode = 71
versionName = "2.9.15" versionName = "2.9.17"
multiDexEnabled true multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests" testApplicationId = "com.kunzisoft.keepass.tests"

View File

@@ -39,6 +39,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.appbar.CollapsingToolbarLayout import com.google.android.material.appbar.CollapsingToolbarLayout
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.SpecialMode import com.kunzisoft.keepass.activities.helpers.SpecialMode
import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
@@ -93,6 +94,8 @@ class EntryActivity : LockingActivity() {
private var clipboardHelper: ClipboardHelper? = null private var clipboardHelper: ClipboardHelper? = null
private var mFirstLaunchOfActivity: Boolean = false private var mFirstLaunchOfActivity: Boolean = false
private var mExternalFileHelper: ExternalFileHelper? = null
private var iconColor: Int = 0 private var iconColor: Int = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -140,6 +143,9 @@ class EntryActivity : LockingActivity() {
clipboardHelper = ClipboardHelper(this) clipboardHelper = ClipboardHelper(this)
mFirstLaunchOfActivity = savedInstanceState?.getBoolean(KEY_FIRST_LAUNCH_ACTIVITY) ?: true mFirstLaunchOfActivity = savedInstanceState?.getBoolean(KEY_FIRST_LAUNCH_ACTIVITY) ?: true
// Init SAF manager
mExternalFileHelper = ExternalFileHelper(this)
// Init attachment service binder manager // Init attachment service binder manager
mAttachmentFileBinderManager = AttachmentFileBinderManager(this) mAttachmentFileBinderManager = AttachmentFileBinderManager(this)
@@ -344,7 +350,7 @@ class EntryActivity : LockingActivity() {
// Manage attachments // Manage attachments
entryContentsView?.assignAttachments(entryInfo.attachments.toSet(), StreamDirection.DOWNLOAD) { attachmentItem -> entryContentsView?.assignAttachments(entryInfo.attachments.toSet(), StreamDirection.DOWNLOAD) { attachmentItem ->
createDocument(this, attachmentItem.name)?.let { requestCode -> mExternalFileHelper?.createDocument(attachmentItem.name)?.let { requestCode ->
mAttachmentsToDownload[requestCode] = attachmentItem mAttachmentsToDownload[requestCode] = attachmentItem
} }
} }
@@ -380,7 +386,7 @@ class EntryActivity : LockingActivity() {
} }
} }
onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri -> mExternalFileHelper?.onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri ->
if (createdFileUri != null) { if (createdFileUri != null) {
mAttachmentsToDownload[requestCode]?.let { attachmentToDownload -> mAttachmentsToDownload[requestCode]?.let { attachmentToDownload ->
mAttachmentFileBinderManager mAttachmentFileBinderManager

View File

@@ -45,7 +45,7 @@ import com.kunzisoft.keepass.activities.dialogs.*
import com.kunzisoft.keepass.activities.dialogs.FileTooBigDialogFragment.Companion.MAX_WARNING_BINARY_FILE import com.kunzisoft.keepass.activities.dialogs.FileTooBigDialogFragment.Companion.MAX_WARNING_BINARY_FILE
import com.kunzisoft.keepass.activities.fragments.EntryEditFragment import com.kunzisoft.keepass.activities.fragments.EntryEditFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
import com.kunzisoft.keepass.autofill.AutofillComponent import com.kunzisoft.keepass.autofill.AutofillComponent
@@ -103,7 +103,7 @@ class EntryEditActivity : LockingActivity(),
private var lockView: View? = null private var lockView: View? = null
// To manage attachments // To manage attachments
private var mSelectFileHelper: SelectFileHelper? = null private var mExternalFileHelper: ExternalFileHelper? = null
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
private var mAllowMultipleAttachments: Boolean = false private var mAllowMultipleAttachments: Boolean = false
private var mTempAttachments = ArrayList<EntryAttachmentState>() private var mTempAttachments = ArrayList<EntryAttachmentState>()
@@ -241,7 +241,7 @@ class EntryEditActivity : LockingActivity(),
} }
// To retrieve attachment // To retrieve attachment
mSelectFileHelper = SelectFileHelper(this) mExternalFileHelper = ExternalFileHelper(this)
mAttachmentFileBinderManager = AttachmentFileBinderManager(this) mAttachmentFileBinderManager = AttachmentFileBinderManager(this)
// Save button // Save button
@@ -458,8 +458,8 @@ class EntryEditActivity : LockingActivity(),
/** /**
* Add a new attachment * Add a new attachment
*/ */
private fun addNewAttachment(item: MenuItem) { private fun addNewAttachment() {
mSelectFileHelper?.selectFileOnClickViewListener?.onMenuItemClick(item) mExternalFileHelper?.openDocument()
} }
override fun onValidateUploadFileTooBig(attachmentToUploadUri: Uri?, fileName: String?) { override fun onValidateUploadFileTooBig(attachmentToUploadUri: Uri?, fileName: String?) {
@@ -505,7 +505,7 @@ class EntryEditActivity : LockingActivity(),
entryEditFragment?.icon = icon entryEditFragment?.icon = icon
} }
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
uri?.let { attachmentToUploadUri -> uri?.let { attachmentToUploadUri ->
UriUtil.getFileData(this, attachmentToUploadUri)?.also { documentFile -> UriUtil.getFileData(this, attachmentToUploadUri)?.also { documentFile ->
documentFile.name?.let { fileName -> documentFile.name?.let { fileName ->
@@ -655,7 +655,7 @@ class EntryEditActivity : LockingActivity(),
&& entryEditActivityEducation.checkAndPerformedAttachmentEducation( && entryEditActivityEducation.checkAndPerformedAttachmentEducation(
attachmentView, attachmentView,
{ {
mSelectFileHelper?.selectFileOnClickViewListener?.onClick(attachmentView) mExternalFileHelper?.openDocument()
}, },
{ {
performedNextEducation(entryEditActivityEducation) performedNextEducation(entryEditActivityEducation)
@@ -683,7 +683,7 @@ class EntryEditActivity : LockingActivity(),
return true return true
} }
R.id.menu_add_attachment -> { R.id.menu_add_attachment -> {
addNewAttachment(item) addNewAttachment()
return true return true
} }
R.id.menu_add_otp -> { R.id.menu_add_otp -> {

View File

@@ -42,8 +42,9 @@ import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.SpecialMode import com.kunzisoft.keepass.activities.helpers.SpecialMode
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
import com.kunzisoft.keepass.activities.selection.SpecialModeActivity import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
@@ -82,7 +83,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
private var mDatabaseFileUri: Uri? = null private var mDatabaseFileUri: Uri? = null
private var mSelectFileHelper: SelectFileHelper? = null private var mExternalFileHelper: ExternalFileHelper? = null
private var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null private var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
@@ -103,14 +104,9 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
createDatabaseButtonView?.setOnClickListener { createNewFile() } createDatabaseButtonView?.setOnClickListener { createNewFile() }
// Open database button // Open database button
mSelectFileHelper = SelectFileHelper(this) mExternalFileHelper = ExternalFileHelper(this)
openDatabaseButtonView = findViewById(R.id.open_keyfile_button) openDatabaseButtonView = findViewById(R.id.open_keyfile_button)
openDatabaseButtonView?.apply { openDatabaseButtonView?.setOpenDocumentClickListener(mExternalFileHelper)
mSelectFileHelper?.selectFileOnClickViewListener?.let {
setOnClickListener(it)
setOnLongClickListener(it)
}
}
// History list // History list
val fileDatabaseHistoryRecyclerView = findViewById<RecyclerView>(R.id.file_list) val fileDatabaseHistoryRecyclerView = findViewById<RecyclerView>(R.id.file_list)
@@ -162,29 +158,31 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
// Observe list of databases // Observe list of databases
databaseFilesViewModel.databaseFilesLoaded.observe(this) { databaseFiles -> databaseFilesViewModel.databaseFilesLoaded.observe(this) { databaseFiles ->
when (databaseFiles.databaseFileAction) { try {
DatabaseFilesViewModel.DatabaseFileAction.NONE -> { when (databaseFiles.databaseFileAction) {
mAdapterDatabaseHistory?.replaceAllDatabaseFileHistoryList(databaseFiles.databaseFileList) DatabaseFilesViewModel.DatabaseFileAction.NONE -> {
} mAdapterDatabaseHistory?.replaceAllDatabaseFileHistoryList(databaseFiles.databaseFileList)
DatabaseFilesViewModel.DatabaseFileAction.ADD -> {
databaseFiles.databaseFileToActivate?.let { databaseFileToAdd ->
mAdapterDatabaseHistory?.addDatabaseFileHistory(databaseFileToAdd)
} }
GroupActivity.launch(this@FileDatabaseSelectActivity, DatabaseFilesViewModel.DatabaseFileAction.ADD -> {
PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity)) databaseFiles.databaseFileToActivate?.let { databaseFileToAdd ->
} mAdapterDatabaseHistory?.addDatabaseFileHistory(databaseFileToAdd)
DatabaseFilesViewModel.DatabaseFileAction.UPDATE -> { }
databaseFiles.databaseFileToActivate?.let { databaseFileToUpdate -> }
mAdapterDatabaseHistory?.updateDatabaseFileHistory(databaseFileToUpdate) DatabaseFilesViewModel.DatabaseFileAction.UPDATE -> {
} databaseFiles.databaseFileToActivate?.let { databaseFileToUpdate ->
} mAdapterDatabaseHistory?.updateDatabaseFileHistory(databaseFileToUpdate)
DatabaseFilesViewModel.DatabaseFileAction.DELETE -> { }
databaseFiles.databaseFileToActivate?.let { databaseFileToDelete -> }
mAdapterDatabaseHistory?.deleteDatabaseFileHistory(databaseFileToDelete) DatabaseFilesViewModel.DatabaseFileAction.DELETE -> {
databaseFiles.databaseFileToActivate?.let { databaseFileToDelete ->
mAdapterDatabaseHistory?.deleteDatabaseFileHistory(databaseFileToDelete)
}
} }
} }
databaseFilesViewModel.consumeAction()
} catch (e: Exception) {
Log.e(TAG, "Unable to observe database action", e)
} }
databaseFilesViewModel.consumeAction()
} }
// Observe default database // Observe default database
@@ -202,6 +200,8 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
val mainCredential = result.data?.getParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY) ?: MainCredential() val mainCredential = result.data?.getParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY) ?: MainCredential()
databaseFilesViewModel.addDatabaseFile(databaseUri, mainCredential.keyFileUri) databaseFilesViewModel.addDatabaseFile(databaseUri, mainCredential.keyFileUri)
} }
GroupActivity.launch(this@FileDatabaseSelectActivity,
PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity))
} }
ACTION_DATABASE_LOAD_TASK -> { ACTION_DATABASE_LOAD_TASK -> {
val database = Database.getInstance() val database = Database.getInstance()
@@ -230,7 +230,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
* Create a new file by calling the content provider * Create a new file by calling the content provider
*/ */
private fun createNewFile() { private fun createNewFile() {
createDocument(this, getString(R.string.database_file_name_default) + mExternalFileHelper?.createDocument( getString(R.string.database_file_name_default) +
getString(R.string.database_file_extension_default), "application/x-keepass") getString(R.string.database_file_extension_default), "application/x-keepass")
} }
@@ -282,7 +282,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
// Show open and create button or special mode // Show open and create button or special mode
when (mSpecialMode) { when (mSpecialMode) {
SpecialMode.DEFAULT -> { SpecialMode.DEFAULT -> {
if (allowCreateDocumentByStorageAccessFramework(packageManager)) { if (ExternalFileHelper.allowCreateDocumentByStorageAccessFramework(packageManager)) {
// There is an activity which can handle this intent. // There is an activity which can handle this intent.
createDatabaseButtonView?.visibility = View.VISIBLE createDatabaseButtonView?.visibility = View.VISIBLE
} else{ } else{
@@ -355,14 +355,14 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
} }
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
if (uri != null) { if (uri != null) {
launchPasswordActivityWithPath(uri) launchPasswordActivityWithPath(uri)
} }
} }
// Retrieve the created URI from the file manager // Retrieve the created URI from the file manager
onCreateDocumentResult(requestCode, resultCode, data) { databaseFileCreatedUri -> mExternalFileHelper?.onCreateDocumentResult(requestCode, resultCode, data) { databaseFileCreatedUri ->
mDatabaseFileUri = databaseFileCreatedUri mDatabaseFileUri = databaseFileCreatedUri
if (mDatabaseFileUri != null) { if (mDatabaseFileUri != null) {
AssignMasterKeyDialogFragment.getInstance(true) AssignMasterKeyDialogFragment.getInstance(true)
@@ -408,9 +408,9 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
openDatabaseButtonView != null openDatabaseButtonView != null
&& fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation( && fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation(
openDatabaseButtonView!!, openDatabaseButtonView!!,
{tapTargetView -> { tapTargetView ->
tapTargetView?.let { tapTargetView?.let {
mSelectFileHelper?.selectFileOnClickViewListener?.onClick(it) mExternalFileHelper?.openDocument()
} }
}, },
{} {}

View File

@@ -34,7 +34,8 @@ import androidx.fragment.app.commit
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.fragments.IconPickerFragment import com.kunzisoft.keepass.activities.fragments.IconPickerFragment
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
@@ -66,7 +67,7 @@ class IconPickerActivity : LockingActivity() {
private var mDatabase: Database? = null private var mDatabase: Database? = null
private var mSelectFileHelper: SelectFileHelper? = null private var mExternalFileHelper: ExternalFileHelper? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -84,15 +85,11 @@ class IconPickerActivity : LockingActivity() {
coordinatorLayout = findViewById(R.id.icon_picker_coordinator) coordinatorLayout = findViewById(R.id.icon_picker_coordinator)
mExternalFileHelper = ExternalFileHelper(this)
uploadButton = findViewById(R.id.icon_picker_upload) uploadButton = findViewById(R.id.icon_picker_upload)
if (mDatabase?.allowCustomIcons == true) { if (mDatabase?.allowCustomIcons == true) {
uploadButton.setOnClickListener { uploadButton.setOpenDocumentClickListener(mExternalFileHelper)
mSelectFileHelper?.selectFileOnClickViewListener?.onClick(it)
}
uploadButton.setOnLongClickListener {
mSelectFileHelper?.selectFileOnClickViewListener?.onLongClick(it)
true
}
} else { } else {
uploadButton.visibility = View.GONE uploadButton.visibility = View.GONE
} }
@@ -124,8 +121,6 @@ class IconPickerActivity : LockingActivity() {
// Focus view to reinitialize timeout // Focus view to reinitialize timeout
findViewById<ViewGroup>(R.id.icon_picker_container)?.resetAppTimeoutWhenViewFocusedOrChanged(this) findViewById<ViewGroup>(R.id.icon_picker_container)?.resetAppTimeoutWhenViewFocusedOrChanged(this)
mSelectFileHelper = SelectFileHelper(this)
iconPickerViewModel.standardIconPicked.observe(this) { iconStandard -> iconPickerViewModel.standardIconPicked.observe(this) { iconStandard ->
mIconImage.standard = iconStandard mIconImage.standard = iconStandard
// Remove the custom icon if a standard one is selected // Remove the custom icon if a standard one is selected
@@ -281,7 +276,7 @@ class IconPickerActivity : LockingActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
addCustomIcon(uri) addCustomIcon(uri)
} }
} }

View File

@@ -42,10 +42,7 @@ import androidx.fragment.app.commit
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.*
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper
import com.kunzisoft.keepass.activities.helpers.SpecialMode
import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.selection.SpecialModeActivity import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
@@ -95,7 +92,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
private var mDatabaseKeyFileUri: Uri? = null private var mDatabaseKeyFileUri: Uri? = null
private var mRememberKeyFile: Boolean = false private var mRememberKeyFile: Boolean = false
private var mSelectFileHelper: SelectFileHelper? = null private var mExternalFileHelper: ExternalFileHelper? = null
private var mPermissionAsked = false private var mPermissionAsked = false
private var readOnly: Boolean = false private var readOnly: Boolean = false
@@ -138,13 +135,8 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState) readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState)
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this) mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this)
mSelectFileHelper = SelectFileHelper(this@PasswordActivity) mExternalFileHelper = ExternalFileHelper(this@PasswordActivity)
keyFileSelectionView?.apply { keyFileSelectionView?.setOpenDocumentClickListener(mExternalFileHelper)
mSelectFileHelper?.selectFileOnClickViewListener?.let {
setOnClickListener(it)
setOnLongClickListener(it)
}
}
passwordView?.setOnEditorActionListener(onEditorActionListener) passwordView?.setOnEditorActionListener(onEditorActionListener)
passwordView?.addTextChangedListener(object : TextWatcher { passwordView?.addTextChangedListener(object : TextWatcher {
@@ -702,8 +694,8 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
} }
var keyFileResult = false var keyFileResult = false
mSelectFileHelper?.let { mExternalFileHelper?.let {
keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data keyFileResult = it.onOpenDocumentResult(requestCode, resultCode, data
) { uri -> ) { uri ->
if (uri != null) { if (uri != null) {
mDatabaseKeyFileUri = uri mDatabaseKeyFileUri = uri

View File

@@ -30,13 +30,13 @@ import android.text.SpannableStringBuilder
import android.text.TextWatcher import android.text.TextWatcher
import android.view.View import android.view.View
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
import com.kunzisoft.keepass.model.MainCredential import com.kunzisoft.keepass.model.MainCredential
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.view.KeyFileSelectionView import com.kunzisoft.keepass.view.KeyFileSelectionView
@@ -60,7 +60,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
private var mListener: AssignPasswordDialogListener? = null private var mListener: AssignPasswordDialogListener? = null
private var mSelectFileHelper: SelectFileHelper? = null private var mExternalFileHelper: ExternalFileHelper? = null
private var mEmptyPasswordConfirmationDialog: AlertDialog? = null private var mEmptyPasswordConfirmationDialog: AlertDialog? = null
private var mNoKeyConfirmationDialog: AlertDialog? = null private var mNoKeyConfirmationDialog: AlertDialog? = null
@@ -133,11 +133,8 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox) keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox)
keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection) keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection)
mSelectFileHelper = SelectFileHelper(this) mExternalFileHelper = ExternalFileHelper(this)
keyFileSelectionView?.apply { keyFileSelectionView?.setOpenDocumentClickListener(mExternalFileHelper)
setOnClickListener(mSelectFileHelper?.selectFileOnClickViewListener)
setOnLongClickListener(mSelectFileHelper?.selectFileOnClickViewListener)
}
val dialog = builder.create() val dialog = builder.create()
@@ -289,7 +286,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
uri?.let { pathUri -> uri?.let { pathUri ->
UriUtil.getFileData(requireContext(), uri)?.length()?.let { lengthFile -> UriUtil.getFileData(requireContext(), uri)?.length()?.let { lengthFile ->
keyFileSelectionView?.error = null keyFileSelectionView?.error = null

View File

@@ -0,0 +1,254 @@
/*
* Copyright 2019 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 <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.helpers
import android.annotation.SuppressLint
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.util.Log
import android.view.View
import androidx.annotation.RequiresApi
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment
import com.kunzisoft.keepass.utils.UriUtil
class ExternalFileHelper {
private var activity: FragmentActivity? = null
private var fragment: Fragment? = null
constructor(context: FragmentActivity) {
this.activity = context
this.fragment = null
}
constructor(context: Fragment) {
this.activity = context.activity
this.fragment = context
}
fun openDocument(getContent: Boolean = false,
typeString: String = "*/*") {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
if (getContent) {
openActivityWithActionGetContent(typeString)
} else {
openActivityWithActionOpenDocument(typeString)
}
} catch (e: Exception) {
Log.e(TAG, "Unable to open document", e)
showFileManagerDialogFragment()
}
} else {
showFileManagerDialogFragment()
}
}
@RequiresApi(Build.VERSION_CODES.KITKAT)
private fun openActivityWithActionOpenDocument(typeString: String) {
val intentOpenDocument = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = typeString
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
}
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
if (fragment != null)
fragment?.startActivityForResult(intentOpenDocument, OPEN_DOC)
else
activity?.startActivityForResult(intentOpenDocument, OPEN_DOC)
}
@RequiresApi(Build.VERSION_CODES.KITKAT)
private fun openActivityWithActionGetContent(typeString: String) {
val intentGetContent = Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = typeString
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
}
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
if (fragment != null)
fragment?.startActivityForResult(intentGetContent, GET_CONTENT)
else
activity?.startActivityForResult(intentGetContent, GET_CONTENT)
}
/**
* To use in onActivityResultCallback in Fragment or Activity
* @param onFileSelected Callback retrieve from data
* @return true if requestCode was captured, false elsewhere
*/
fun onOpenDocumentResult(requestCode: Int, resultCode: Int, data: Intent?,
onFileSelected: ((uri: Uri?) -> Unit)?): Boolean {
when (requestCode) {
FILE_BROWSE -> {
if (resultCode == RESULT_OK) {
val filename = data?.dataString
var keyUri: Uri? = null
if (filename != null) {
keyUri = UriUtil.parse(filename)
}
onFileSelected?.invoke(keyUri)
}
return true
}
GET_CONTENT, OPEN_DOC -> {
if (resultCode == RESULT_OK) {
if (data != null) {
val uri = data.data
if (uri != null) {
try {
// try to persist read and write permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity?.contentResolver?.apply {
takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
}
} catch (e: Exception) {
// nop
}
onFileSelected?.invoke(uri)
}
}
}
return true
}
}
return false
}
/**
* Show Browser dialog to select file picker app
*/
private fun showFileManagerDialogFragment() {
try {
if (fragment != null) {
fragment?.parentFragmentManager
} else {
activity?.supportFragmentManager
}?.let { fragmentManager ->
FileManagerDialogFragment().show(fragmentManager, "browserDialog")
}
} catch (e: Exception) {
Log.e(TAG, "Can't open BrowserDialog", e)
}
}
fun createDocument(titleString: String,
typeString: String = "application/octet-stream"): Int? {
val idCode = getUnusedCreateFileRequestCode()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = typeString
putExtra(Intent.EXTRA_TITLE, titleString)
}
if (fragment != null)
fragment?.startActivityForResult(intent, idCode)
else
activity?.startActivityForResult(intent, idCode)
return idCode
} catch (e: Exception) {
Log.e(TAG, "Unable to create document", e)
showFileManagerDialogFragment()
}
} else {
showFileManagerDialogFragment()
}
return null
}
/**
* To use in onActivityResultCallback in Fragment or Activity
* @param onFileCreated Callback retrieve from data
* @return true if requestCode was captured, false elsewhere
*/
fun onCreateDocumentResult(requestCode: Int, resultCode: Int, data: Intent?,
onFileCreated: (fileCreated: Uri?)->Unit) {
// Retrieve the created URI from the file manager
if (fileRequestCodes.contains(requestCode) && resultCode == RESULT_OK) {
onFileCreated.invoke(data?.data)
fileRequestCodes.remove(requestCode)
}
}
companion object {
private const val TAG = "OpenFileHelper"
private const val GET_CONTENT = 25745
private const val OPEN_DOC = 25845
private const val FILE_BROWSE = 25645
private var CREATE_FILE_REQUEST_CODE_DEFAULT = 3853
private var fileRequestCodes = ArrayList<Int>()
private fun getUnusedCreateFileRequestCode(): Int {
val newCreateFileRequestCode = CREATE_FILE_REQUEST_CODE_DEFAULT++
fileRequestCodes.add(newCreateFileRequestCode)
return newCreateFileRequestCode
}
@SuppressLint("InlinedApi")
fun allowCreateDocumentByStorageAccessFramework(packageManager: PackageManager,
typeString: String = "application/octet-stream"): Boolean {
return when {
// To check if a custom file manager can manage the ACTION_CREATE_DOCUMENT
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> {
packageManager.queryIntentActivities(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = typeString
}, PackageManager.MATCH_DEFAULT_ONLY).isNotEmpty()
}
else -> true
}
}
}
}
fun View.setOpenDocumentClickListener(externalFileHelper: ExternalFileHelper?) {
externalFileHelper?.let { fileHelper ->
setOnClickListener {
fileHelper.openDocument()
}
setOnLongClickListener {
fileHelper.openDocument(true)
true
}
} ?: kotlin.run {
setOnClickListener(null)
setOnLongClickListener(null)
}
}

View File

@@ -1,244 +0,0 @@
/*
* Copyright 2019 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 <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.activities.helpers
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Activity.RESULT_OK
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.util.Log
import android.view.MenuItem
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment
import com.kunzisoft.keepass.utils.UriUtil
class SelectFileHelper {
private var activity: Activity? = null
private var fragment: Fragment? = null
val selectFileOnClickViewListener: SelectFileOnClickViewListener
get() = SelectFileOnClickViewListener()
constructor(context: Activity) {
this.activity = context
this.fragment = null
}
constructor(context: Fragment) {
this.activity = context.activity
this.fragment = context
}
inner class SelectFileOnClickViewListener :
View.OnClickListener,
View.OnLongClickListener,
MenuItem.OnMenuItemClickListener {
private fun onAbstractClick(longClick: Boolean = false) {
try {
if (longClick) {
try {
openActivityWithActionGetContent()
} catch (e: Exception) {
openActivityWithActionOpenDocument()
}
} else {
try {
openActivityWithActionOpenDocument()
} catch (e: Exception) {
openActivityWithActionGetContent()
}
}
} catch (e: Exception) {
Log.e(TAG, "Enable to start the file picker activity", e)
// Open browser dialog
if (lookForOpenIntentsFilePicker())
showBrowserDialog()
}
}
override fun onClick(v: View) {
onAbstractClick()
}
override fun onLongClick(v: View?): Boolean {
onAbstractClick(true)
return true
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
onAbstractClick()
return true
}
}
@SuppressLint("InlinedApi")
private fun openActivityWithActionOpenDocument() {
val intentOpenDocument = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
if (fragment != null)
fragment?.startActivityForResult(intentOpenDocument, OPEN_DOC)
else
activity?.startActivityForResult(intentOpenDocument, OPEN_DOC)
}
@SuppressLint("InlinedApi")
private fun openActivityWithActionGetContent() {
val intentGetContent = Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
if (fragment != null)
fragment?.startActivityForResult(intentGetContent, GET_CONTENT)
else
activity?.startActivityForResult(intentGetContent, GET_CONTENT)
}
private fun lookForOpenIntentsFilePicker(): Boolean {
var showBrowser = false
try {
if (isIntentAvailable(activity!!, OPEN_INTENTS_FILE_BROWSE)) {
val intent = Intent(OPEN_INTENTS_FILE_BROWSE)
if (fragment != null)
fragment?.startActivityForResult(intent, FILE_BROWSE)
else
activity?.startActivityForResult(intent, FILE_BROWSE)
} else {
showBrowser = true
}
} catch (e: Exception) {
Log.w(TAG, "Enable to start OPEN_INTENTS_FILE_BROWSE", e)
showBrowser = true
}
return showBrowser
}
/**
* Indicates whether the specified action can be used as an intent. This
* method queries the package manager for installed packages that can
* respond to an intent with the specified action. If no suitable package is
* found, this method returns false.
*
* @param context The application's environment.
* @param action The Intent action to check for availability.
*
* @return True if an Intent with the specified action can be sent and
* responded to, false otherwise.
*/
private fun isIntentAvailable(context: Context, action: String): Boolean {
val packageManager = context.packageManager
val intent = Intent(action)
val list = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY)
return list.size > 0
}
/**
* Show Browser dialog to select file picker app
*/
private fun showBrowserDialog() {
try {
val fileManagerDialogFragment = FileManagerDialogFragment()
fragment?.let {
fileManagerDialogFragment.show(it.parentFragmentManager, "browserDialog")
} ?: fileManagerDialogFragment.show((activity as FragmentActivity).supportFragmentManager, "browserDialog")
} catch (e: Exception) {
Log.e(TAG, "Can't open BrowserDialog", e)
}
}
/**
* To use in onActivityResultCallback in Fragment or Activity
* @param keyFileCallback Callback retrieve from data
* @return true if requestCode was captured, false elsechere
*/
fun onActivityResultCallback(
requestCode: Int,
resultCode: Int,
data: Intent?,
keyFileCallback: ((uri: Uri?) -> Unit)?): Boolean {
when (requestCode) {
FILE_BROWSE -> {
if (resultCode == RESULT_OK) {
val filename = data?.dataString
var keyUri: Uri? = null
if (filename != null) {
keyUri = UriUtil.parse(filename)
}
keyFileCallback?.invoke(keyUri)
}
return true
}
GET_CONTENT, OPEN_DOC -> {
if (resultCode == RESULT_OK) {
if (data != null) {
val uri = data.data
if (uri != null) {
try {
// try to persist read and write permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity?.contentResolver?.apply {
takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
}
} catch (e: Exception) {
// nop
}
keyFileCallback?.invoke(uri)
}
}
}
return true
}
}
return false
}
companion object {
private const val TAG = "OpenFileHelper"
const val OPEN_INTENTS_FILE_BROWSE = "org.openintents.action.PICK_FILE"
private const val GET_CONTENT = 25745
private const val OPEN_DOC = 25845
private const val FILE_BROWSE = 25645
}
}

View File

@@ -37,12 +37,12 @@ object Stylish {
* Initialize the class with a theme preference * Initialize the class with a theme preference
* @param context Context to retrieve the theme preference * @param context Context to retrieve the theme preference
*/ */
fun init(context: Context) { fun load(context: Context) {
Log.d(Stylish::class.java.name, "Attatching to " + context.packageName) Log.d(Stylish::class.java.name, "Attatching to " + context.packageName)
themeString = PreferencesUtil.getStyle(context) themeString = PreferencesUtil.getStyle(context)
} }
private fun retrieveEquivalentSystemStyle(context: Context, styleString: String): String { fun retrieveEquivalentSystemStyle(context: Context, styleString: String): String {
val systemNightMode = when (PreferencesUtil.getStyleBrightness(context)) { val systemNightMode = when (PreferencesUtil.getStyleBrightness(context)) {
context.getString(R.string.list_style_brightness_light) -> false context.getString(R.string.list_style_brightness_light) -> false
context.getString(R.string.list_style_brightness_night) -> true context.getString(R.string.list_style_brightness_night) -> true
@@ -84,12 +84,16 @@ object Stylish {
} }
} }
fun defaultStyle(context: Context): String {
return context.getString(R.string.list_style_name_light)
}
/** /**
* Assign the style to the class attribute * Assign the style to the class attribute
* @param styleString Style id String * @param styleString Style id String
*/ */
fun assignStyle(context: Context, styleString: String) { fun assignStyle(context: Context, styleString: String) {
themeString = retrieveEquivalentSystemStyle(context, styleString) PreferencesUtil.setStyle(context, styleString)
} }
/** /**

View File

@@ -29,7 +29,7 @@ class App : MultiDexApplication() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Stylish.init(this) Stylish.load(this)
PRNGFixes.apply() PRNGFixes.apply()
} }

View File

@@ -25,6 +25,7 @@ import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.net.Uri import android.net.Uri
import android.os.IBinder import android.os.IBinder
import android.util.Log
import com.kunzisoft.keepass.services.AdvancedUnlockNotificationService import com.kunzisoft.keepass.services.AdvancedUnlockNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.SingletonHolderParameter import com.kunzisoft.keepass.utils.SingletonHolderParameter
@@ -76,7 +77,11 @@ class CipherDatabaseAction(context: Context) {
mServiceConnection!!, mServiceConnection!!,
Context.BIND_ABOVE_CLIENT) Context.BIND_ABOVE_CLIENT)
if (mBinder == null) { if (mBinder == null) {
applicationContext.startService(mIntentAdvancedUnlockService) try {
applicationContext.startService(mIntentAdvancedUnlockService)
} catch (e: Exception) {
Log.e(TAG, "Unable to start cipher action", e)
}
} }
} }
} }
@@ -173,5 +178,7 @@ class CipherDatabaseAction(context: Context) {
).execute() ).execute()
} }
companion object : SingletonHolderParameter<CipherDatabaseAction, Context>(::CipherDatabaseAction) companion object : SingletonHolderParameter<CipherDatabaseAction, Context>(::CipherDatabaseAction) {
private val TAG = CipherDatabaseAction::class.java.name
}
} }

View File

@@ -25,7 +25,10 @@ import android.content.Context.BIND_NOT_FOREGROUND
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.util.Log
import android.widget.Toast
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
@@ -251,11 +254,16 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
} }
private fun start(bundle: Bundle? = null, actionTask: String) { private fun start(bundle: Bundle? = null, actionTask: String) {
activity.stopService(intentDatabaseTask) try {
if (bundle != null) activity.stopService(intentDatabaseTask)
intentDatabaseTask.putExtras(bundle) if (bundle != null)
intentDatabaseTask.action = actionTask intentDatabaseTask.putExtras(bundle)
activity.startService(intentDatabaseTask) intentDatabaseTask.action = actionTask
activity.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()
}
} }
/* /*
@@ -591,4 +599,8 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
} }
, ACTION_DATABASE_SAVE) , ACTION_DATABASE_SAVE)
} }
companion object {
private val TAG = ProgressDatabaseTaskProvider::class.java.name
}
} }

View File

@@ -36,6 +36,9 @@ abstract class CipherEngine {
return 16 return 16
} }
// Used only with padding workaround
var forcePaddingCompatibility = false
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class) @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
abstract fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher abstract fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher

View File

@@ -30,7 +30,7 @@ class TwofishEngine : CipherEngine() {
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class) @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
override fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher { override fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher {
return CipherFactory.getTwofish(opmode, key, IV) return CipherFactory.getTwofish(opmode, key, IV, forcePaddingCompatibility)
} }
override fun getEncryptionAlgorithm(): EncryptionAlgorithm { override fun getEncryptionAlgorithm(): EncryptionAlgorithm {

View File

@@ -105,10 +105,6 @@ class Database {
return mDatabaseKDB?.binaryCache ?: mDatabaseKDBX?.binaryCache ?: BinaryCache() return mDatabaseKDB?.binaryCache ?: mDatabaseKDBX?.binaryCache ?: BinaryCache()
} }
fun setCacheDirectory(cacheDirectory: File) {
binaryCache.cacheDirectory = cacheDirectory
}
private val iconsManager: IconsManager private val iconsManager: IconsManager
get() { get() {
return mDatabaseKDB?.iconsManager ?: mDatabaseKDBX?.iconsManager ?: IconsManager(binaryCache) return mDatabaseKDB?.iconsManager ?: mDatabaseKDBX?.iconsManager ?: IconsManager(binaryCache)

View File

@@ -466,16 +466,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
return result return result
} }
companion object {
companion object CREATOR : Parcelable.Creator<Entry> {
override fun createFromParcel(parcel: Parcel): Entry {
return Entry(parcel)
}
override fun newArray(size: Int): Array<Entry?> {
return arrayOfNulls(size)
}
const val PMS_TAN_ENTRY = "<TAN>" const val PMS_TAN_ENTRY = "<TAN>"
/** /**
@@ -484,5 +475,16 @@ class Entry : Node, EntryVersionedInterface<Group> {
fun newExtraFieldNameAllowed(field: Field): Boolean { fun newExtraFieldNameAllowed(field: Field): Boolean {
return EntryKDBX.newCustomNameAllowed(field.name) return EntryKDBX.newCustomNameAllowed(field.name)
} }
@JvmField
val CREATOR: Parcelable.Creator<Entry> = object : Parcelable.Creator<Entry> {
override fun createFromParcel(parcel: Parcel): Entry {
return Entry(parcel)
}
override fun newArray(size: Int): Array<Entry?> {
return arrayOfNulls(size)
}
}
} }
} }

View File

@@ -11,7 +11,7 @@ class BinaryCache {
*/ */
var loadedCipherKey: LoadedKey = LoadedKey.generateNewCipherKey() var loadedCipherKey: LoadedKey = LoadedKey.generateNewCipherKey()
lateinit var cacheDirectory: File var cacheDirectory: File? = null
private val voidBinary = KeyByteArray(UNKNOWN, ByteArray(0)) private val voidBinary = KeyByteArray(UNKNOWN, ByteArray(0))
@@ -19,15 +19,16 @@ class BinaryCache {
smallSize: Boolean = false, smallSize: Boolean = false,
compression: Boolean = false, compression: Boolean = false,
protection: Boolean = false): BinaryData { protection: Boolean = false): BinaryData {
return if (smallSize) { val cacheDir = cacheDirectory
return if (smallSize || cacheDir == null) {
BinaryByte(binaryId, compression, protection) BinaryByte(binaryId, compression, protection)
} else { } else {
val fileInCache = File(cacheDirectory, binaryId) val fileInCache = File(cacheDir, binaryId)
return BinaryFile(fileInCache, compression, protection) BinaryFile(fileInCache, compression, protection)
} }
} }
// Similar to file storage but much faster // Similar to file storage but much faster TODO SparseArray
private val byteArrayList = HashMap<String, ByteArray>() private val byteArrayList = HashMap<String, ByteArray>()
fun getByteArray(key: String): KeyByteArray { fun getByteArray(key: String): KeyByteArray {

View File

@@ -90,7 +90,8 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
url = parcel.readString() ?: url url = parcel.readString() ?: url
notes = parcel.readString() ?: notes notes = parcel.readString() ?: notes
binaryDescription = parcel.readString() ?: binaryDescription binaryDescription = parcel.readString() ?: binaryDescription
binaryDataId = parcel.readInt() val rawBinaryDataId = parcel.readInt()
binaryDataId = if (rawBinaryDataId == -1) null else rawBinaryDataId
} }
override fun readParentParcelable(parcel: Parcel): GroupKDB? { override fun readParentParcelable(parcel: Parcel): GroupKDB? {
@@ -109,9 +110,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
dest.writeString(url) dest.writeString(url)
dest.writeString(notes) dest.writeString(notes)
dest.writeString(binaryDescription) dest.writeString(binaryDescription)
binaryDataId?.let { dest.writeInt(binaryDataId ?: -1)
dest.writeInt(it)
}
} }
fun updateWith(source: EntryKDB) { fun updateWith(source: EntryKDB) {

View File

@@ -56,32 +56,6 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
var additional = "" var additional = ""
var tags = "" var tags = ""
fun getSize(attachmentPool: AttachmentPool): Long {
var size = FIXED_LENGTH_SIZE
for (entry in fields.entries) {
size += entry.key.length.toLong()
size += entry.value.length().toLong()
}
size += getAttachmentsSize(attachmentPool)
size += autoType.defaultSequence.length.toLong()
for ((key, value) in autoType.entrySet()) {
size += key.length.toLong()
size += value.length.toLong()
}
for (entry in history) {
size += entry.getSize(attachmentPool)
}
size += overrideURL.length.toLong()
size += tags.length.toLong()
return size
}
override var expires: Boolean = false override var expires: Boolean = false
constructor() : super() constructor() : super()
@@ -102,6 +76,14 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
tags = parcel.readString() ?: tags tags = parcel.readString() ?: tags
} }
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
return parcel.readParcelable(GroupKDBX::class.java.classLoader)
}
override fun writeParentParcelable(parent: GroupKDBX?, parcel: Parcel, flags: Int) {
parcel.writeParcelable(parent, flags)
}
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags) super.writeToParcel(dest, flags)
dest.writeLong(usageCount.toKotlinLong()) dest.writeLong(usageCount.toKotlinLong())
@@ -164,14 +146,6 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
return NodeIdUUID(nodeId.id) return NodeIdUUID(nodeId.id)
} }
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
return parcel.readParcelable(GroupKDBX::class.java.classLoader)
}
override fun writeParentParcelable(parent: GroupKDBX?, parcel: Parcel, flags: Int) {
parcel.writeParcelable(parent, flags)
}
/** /**
* Decode a reference key with the FieldReferencesEngine * Decode a reference key with the FieldReferencesEngine
* @param decodeRef * @param decodeRef
@@ -228,6 +202,32 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
override var locationChanged = DateInstant() override var locationChanged = DateInstant()
fun getSize(attachmentPool: AttachmentPool): Long {
var size = FIXED_LENGTH_SIZE
for (entry in fields.entries) {
size += entry.key.length.toLong()
size += entry.value.length().toLong()
}
size += getAttachmentsSize(attachmentPool)
size += autoType.defaultSequence.length.toLong()
for ((key, value) in autoType.entrySet()) {
size += key.length.toLong()
size += value.length.toLong()
}
for (entry in history) {
size += entry.getSize(attachmentPool)
}
size += overrideURL.length.toLong()
size += tags.length.toLong()
return size
}
fun afterChangeParent() { fun afterChangeParent() {
locationChanged = DateInstant() locationChanged = DateInstant()
} }
@@ -349,6 +349,8 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
const val STR_URL = "URL" const val STR_URL = "URL"
const val STR_NOTES = "Notes" const val STR_NOTES = "Notes"
private const val FIXED_LENGTH_SIZE: Long = 128 // Approximate fixed length size
fun newCustomNameAllowed(name: String): Boolean { fun newCustomNameAllowed(name: String): Boolean {
return !(name.equals(STR_TITLE, true) return !(name.equals(STR_TITLE, true)
|| name.equals(STR_USERNAME, true) || name.equals(STR_USERNAME, true)
@@ -367,7 +369,5 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
return arrayOfNulls(size) return arrayOfNulls(size)
} }
} }
private const val FIXED_LENGTH_SIZE: Long = 128 // Approximate fixed length size
} }
} }

View File

@@ -36,6 +36,10 @@ abstract class EntryVersioned
constructor(parcel: Parcel) : super(parcel) constructor(parcel: Parcel) : super(parcel)
override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags)
}
override fun nodeIndexInParentForNaturalOrder(): Int { override fun nodeIndexInParentForNaturalOrder(): Int {
if (nodeIndexInParentForNaturalOrder == -1) { if (nodeIndexInParentForNaturalOrder == -1) {
val numberOfGroups = parent?.getChildGroups()?.size val numberOfGroups = parent?.getChildGroups()?.size

View File

@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element.icon
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
class IconImage() : IconImageDraw(), Parcelable { class IconImage() : IconImageDraw() {
var standard: IconImageStandard = IconImageStandard() var standard: IconImageStandard = IconImageStandard()
var custom: IconImageCustom = IconImageCustom() var custom: IconImageCustom = IconImageCustom()

View File

@@ -20,11 +20,12 @@
package com.kunzisoft.keepass.database.element.icon package com.kunzisoft.keepass.database.element.icon
import android.os.Parcel import android.os.Parcel
import android.os.ParcelUuid
import android.os.Parcelable import android.os.Parcelable
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
import java.util.* import java.util.*
class IconImageCustom : Parcelable, IconImageDraw { class IconImageCustom : IconImageDraw {
var uuid: UUID var uuid: UUID
@@ -37,17 +38,17 @@ class IconImageCustom : Parcelable, IconImageDraw {
} }
constructor(parcel: Parcel) { constructor(parcel: Parcel) {
uuid = parcel.readSerializable() as UUID uuid = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: DatabaseVersioned.UUID_ZERO
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeParcelable(ParcelUuid(uuid), flags)
} }
override fun describeContents(): Int { override fun describeContents(): Int {
return 0 return 0
} }
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeSerializable(uuid)
}
override fun hashCode(): Int { override fun hashCode(): Int {
val prime = 31 val prime = 31
var result = 1 var result = 1

View File

@@ -19,7 +19,9 @@
*/ */
package com.kunzisoft.keepass.database.element.icon package com.kunzisoft.keepass.database.element.icon
abstract class IconImageDraw { import android.os.Parcelable
abstract class IconImageDraw : Parcelable {
var selected = false var selected = false
/** /**

View File

@@ -23,7 +23,7 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
class IconImageStandard : Parcelable, IconImageDraw { class IconImageStandard : IconImageDraw {
val id: Int val id: Int

View File

@@ -20,6 +20,7 @@
package com.kunzisoft.keepass.database.element.node package com.kunzisoft.keepass.database.element.node
import android.os.Parcel import android.os.Parcel
import android.os.ParcelUuid
import android.os.Parcelable import android.os.Parcelable
import java.util.* import java.util.*
@@ -35,12 +36,12 @@ class NodeIdUUID : NodeId<UUID> {
} }
constructor(parcel: Parcel) { constructor(parcel: Parcel) {
id = parcel.readSerializable() as UUID id = parcel.readParcelable<ParcelUuid>(ParcelUuid::class.java.classLoader)?.uuid ?: id
} }
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags) super.writeToParcel(dest, flags)
dest.writeSerializable(id) dest.writeParcelable(ParcelUuid(id), flags)
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

View File

@@ -151,9 +151,11 @@ class DatabaseInputKDBX(cacheDirectory: File,
val cipher: Cipher val cipher: Cipher
try { try {
engine = EncryptionAlgorithm.getFrom(mDatabase.cipherUuid).cipherEngine engine = EncryptionAlgorithm.getFrom(mDatabase.cipherUuid).cipherEngine
engine.forcePaddingCompatibility = true
mDatabase.setDataEngine(engine) mDatabase.setDataEngine(engine)
mDatabase.encryptionAlgorithm = engine.getEncryptionAlgorithm() mDatabase.encryptionAlgorithm = engine.getEncryptionAlgorithm()
cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.finalKey!!, header.encryptionIV) cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.finalKey!!, header.encryptionIV)
engine.forcePaddingCompatibility = false
} catch (e: Exception) { } catch (e: Exception) {
throw InvalidAlgorithmDatabaseException(e) throw InvalidAlgorithmDatabaseException(e)
} }

View File

@@ -10,7 +10,6 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
import kotlinx.coroutines.*
class AdvancedUnlockNotificationService : NotificationService() { class AdvancedUnlockNotificationService : NotificationService() {
@@ -18,9 +17,6 @@ class AdvancedUnlockNotificationService : NotificationService() {
private var mActionTaskBinder = AdvancedUnlockBinder() private var mActionTaskBinder = AdvancedUnlockBinder()
private var notificationTimeoutMilliSecs: Long = 0
private var mTimerJob: Job? = null
inner class AdvancedUnlockBinder: Binder() { inner class AdvancedUnlockBinder: Binder() {
fun getCipherDatabase(databaseUri: Uri): CipherDatabaseEntity? { fun getCipherDatabase(databaseUri: Uri): CipherDatabaseEntity? {
return mTempCipherDao.firstOrNull { it.databaseUri == databaseUri.toString()} return mTempCipherDao.firstOrNull { it.databaseUri == databaseUri.toString()}
@@ -80,23 +76,11 @@ class AdvancedUnlockNotificationService : NotificationService() {
when (intent?.action) { when (intent?.action) {
ACTION_TIMEOUT -> { ACTION_TIMEOUT -> {
notificationTimeoutMilliSecs = PreferencesUtil.getAdvancedUnlockTimeout(this) val notificationTimeoutMilliSecs = PreferencesUtil.getAdvancedUnlockTimeout(this)
// Not necessarily a foreground service // Not necessarily a foreground service
if (mTimerJob == null && notificationTimeoutMilliSecs != TimeoutHelper.NEVER) { if (mTimerJob == null && notificationTimeoutMilliSecs != TimeoutHelper.NEVER) {
mTimerJob = CoroutineScope(Dispatchers.Main).launch { defineTimerJob(notificationBuilder, notificationTimeoutMilliSecs) {
val maxPos = 100 stopSelf()
val posDurationMills = notificationTimeoutMilliSecs / maxPos
for (pos in maxPos downTo 0) {
notificationBuilder.setProgress(maxPos, pos, false)
startForeground(notificationId, notificationBuilder.build())
delay(posDurationMills)
if (pos <= 0) {
stopSelf()
}
}
notificationManager?.cancel(notificationId)
mTimerJob = null
cancel()
} }
} else { } else {
startForeground(notificationId, notificationBuilder.build()) startForeground(notificationId, notificationBuilder.build())
@@ -118,7 +102,6 @@ class AdvancedUnlockNotificationService : NotificationService() {
override fun onDestroy() { override fun onDestroy() {
mTempCipherDao.clear() mTempCipherDao.clear()
mTimerJob?.cancel()
super.onDestroy() super.onDestroy()
} }

View File

@@ -26,7 +26,6 @@ import android.net.Uri
import android.os.Binder import android.os.Binder
import android.os.IBinder import android.os.IBinder
import android.util.Log import android.util.Log
import androidx.documentfile.provider.DocumentFile
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.Attachment import com.kunzisoft.keepass.database.element.Attachment
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
@@ -34,6 +33,7 @@ import com.kunzisoft.keepass.model.AttachmentState
import com.kunzisoft.keepass.model.EntryAttachmentState import com.kunzisoft.keepass.model.EntryAttachmentState
import com.kunzisoft.keepass.model.StreamDirection import com.kunzisoft.keepass.model.StreamDirection
import com.kunzisoft.keepass.tasks.BinaryDatabaseManager import com.kunzisoft.keepass.tasks.BinaryDatabaseManager
import com.kunzisoft.keepass.utils.UriUtil
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.util.* import java.util.*
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
@@ -173,7 +173,8 @@ class AttachmentFileNotificationService: LockNotificationService() {
putExtra(FILE_URI_KEY, attachmentNotification.uri) putExtra(FILE_URI_KEY, attachmentNotification.uri)
}, PendingIntent.FLAG_CANCEL_CURRENT) }, PendingIntent.FLAG_CANCEL_CURRENT)
val fileName = DocumentFile.fromSingleUri(this, attachmentNotification.uri)?.name ?: "" val fileName = UriUtil.getFileData(this, attachmentNotification.uri)?.name
?: attachmentNotification.uri.path
val builder = buildNewNotification().apply { val builder = buildNewNotification().apply {
when (attachmentNotification.entryAttachmentState.streamDirection) { when (attachmentNotification.entryAttachmentState.streamDirection) {

View File

@@ -37,8 +37,7 @@ class ClipboardEntryNotificationService : LockNotificationService() {
override val notificationId = 485 override val notificationId = 485
private var mEntryInfo: EntryInfo? = null private var mEntryInfo: EntryInfo? = null
private var clipboardHelper: ClipboardHelper? = null private var clipboardHelper: ClipboardHelper? = null
private var notificationTimeoutMilliSecs: Long = 0 private var mNotificationTimeoutMilliSecs: Long = 0
private var cleanCopyNotificationTimerTask: Thread? = null
override fun retrieveChannelId(): String { override fun retrieveChannelId(): String {
return CHANNEL_CLIPBOARD_ID return CHANNEL_CLIPBOARD_ID
@@ -70,7 +69,7 @@ class ClipboardEntryNotificationService : LockNotificationService() {
mEntryInfo = intent?.getParcelableExtra(EXTRA_ENTRY_INFO) mEntryInfo = intent?.getParcelableExtra(EXTRA_ENTRY_INFO)
//Get settings //Get settings
notificationTimeoutMilliSecs = PreferencesUtil.getClipboardTimeout(this) mNotificationTimeoutMilliSecs = PreferencesUtil.getClipboardTimeout(this)
when { when {
intent == null -> Log.w(TAG, "null intent") intent == null -> Log.w(TAG, "null intent")
@@ -78,7 +77,7 @@ class ClipboardEntryNotificationService : LockNotificationService() {
newNotification(mEntryInfo?.title, constructListOfField(intent)) newNotification(mEntryInfo?.title, constructListOfField(intent))
} }
ACTION_CLEAN_CLIPBOARD == intent.action -> { ACTION_CLEAN_CLIPBOARD == intent.action -> {
stopTask(cleanCopyNotificationTimerTask) mTimerJob?.cancel()
cleanClipboard() cleanClipboard()
stopNotificationAndSendLockIfNeeded() stopNotificationAndSendLockIfNeeded()
} }
@@ -121,7 +120,7 @@ class ClipboardEntryNotificationService : LockNotificationService() {
} }
private fun newNotification(title: String?, fieldsToAdd: ArrayList<ClipboardEntryNotificationField>) { private fun newNotification(title: String?, fieldsToAdd: ArrayList<ClipboardEntryNotificationField>) {
stopTask(cleanCopyNotificationTimerTask) mTimerJob?.cancel()
val builder = buildNewNotification() val builder = buildNewNotification()
.setSmallIcon(R.drawable.notification_ic_clipboard_key_24dp) .setSmallIcon(R.drawable.notification_ic_clipboard_key_24dp)
@@ -147,7 +146,7 @@ class ClipboardEntryNotificationService : LockNotificationService() {
} }
private fun copyField(fieldToCopy: ClipboardEntryNotificationField, nextFields: ArrayList<ClipboardEntryNotificationField>) { private fun copyField(fieldToCopy: ClipboardEntryNotificationField, nextFields: ArrayList<ClipboardEntryNotificationField>) {
stopTask(cleanCopyNotificationTimerTask) mTimerJob?.cancel()
try { try {
var generatedValue = fieldToCopy.getGeneratedValue(mEntryInfo) var generatedValue = fieldToCopy.getGeneratedValue(mEntryInfo)
@@ -170,40 +169,23 @@ class ClipboardEntryNotificationService : LockNotificationService() {
this, 0, cleanIntent, PendingIntent.FLAG_UPDATE_CURRENT) this, 0, cleanIntent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setDeleteIntent(cleanPendingIntent) builder.setDeleteIntent(cleanPendingIntent)
val myNotificationId = notificationId if (mNotificationTimeoutMilliSecs != NEVER) {
defineTimerJob(builder, mNotificationTimeoutMilliSecs, {
if (notificationTimeoutMilliSecs != NEVER) { val newGeneratedValue = fieldToCopy.getGeneratedValue(mEntryInfo)
cleanCopyNotificationTimerTask = Thread { // New auto generated value
val maxPos = 100 if (generatedValue != newGeneratedValue) {
val posDurationMills = notificationTimeoutMilliSecs / maxPos generatedValue = newGeneratedValue
for (pos in maxPos downTo 0) { clipboardHelper?.copyToClipboard(fieldToCopy.label, generatedValue)
val newGeneratedValue = fieldToCopy.getGeneratedValue(mEntryInfo)
// New auto generated value
if (generatedValue != newGeneratedValue) {
generatedValue = newGeneratedValue
clipboardHelper?.copyToClipboard(fieldToCopy.label, generatedValue)
}
builder.setProgress(maxPos, pos, false)
notificationManager?.notify(myNotificationId, builder.build())
try {
Thread.sleep(posDurationMills)
} catch (e: InterruptedException) {
break
}
if (pos <= 0) {
stopNotificationAndSendLockIfNeeded()
}
} }
stopTask(cleanCopyNotificationTimerTask) }) {
notificationManager?.cancel(myNotificationId) stopNotificationAndSendLockIfNeeded()
// Clean password only if no next field // Clean password only if no next field
if (nextFields.size <= 0) if (nextFields.size <= 0)
cleanClipboard() cleanClipboard()
} }
cleanCopyNotificationTimerTask?.start()
} else { } else {
// No timer // No timer
notificationManager?.notify(myNotificationId, builder.build()) notificationManager?.notify(notificationId, builder.build())
} }
} catch (e: Exception) { } catch (e: Exception) {
@@ -228,10 +210,6 @@ class ClipboardEntryNotificationService : LockNotificationService() {
override fun onDestroy() { override fun onDestroy() {
cleanClipboard() cleanClipboard()
stopTask(cleanCopyNotificationTimerTask)
cleanCopyNotificationTimerTask = null
super.onDestroy() super.onDestroy()
} }

View File

@@ -35,8 +35,7 @@ import com.kunzisoft.keepass.utils.LOCK_ACTION
class KeyboardEntryNotificationService : LockNotificationService() { class KeyboardEntryNotificationService : LockNotificationService() {
override val notificationId = 486 override val notificationId = 486
private var cleanNotificationTimerTask: Thread? = null private var mNotificationTimeoutMilliSecs: Long = 0
private var notificationTimeoutMilliSecs: Long = 0
private var pendingDeleteIntent: PendingIntent? = null private var pendingDeleteIntent: PendingIntent? = null
@@ -61,7 +60,7 @@ class KeyboardEntryNotificationService : LockNotificationService() {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
//Get settings //Get settings
notificationTimeoutMilliSecs = PreferenceManager.getDefaultSharedPreferences(this) mNotificationTimeoutMilliSecs = PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.keyboard_entry_timeout_key), .getString(getString(R.string.keyboard_entry_timeout_key),
getString(R.string.timeout_default))?.toLong() ?: TimeoutHelper.DEFAULT_TIMEOUT getString(R.string.timeout_default))?.toLong() ?: TimeoutHelper.DEFAULT_TIMEOUT
@@ -107,27 +106,12 @@ class KeyboardEntryNotificationService : LockNotificationService() {
notificationManager?.cancel(notificationId) notificationManager?.cancel(notificationId)
notificationManager?.notify(notificationId, builder.build()) notificationManager?.notify(notificationId, builder.build())
stopTask(cleanNotificationTimerTask)
// Timeout only if notification clear is available // Timeout only if notification clear is available
if (PreferencesUtil.isClearKeyboardNotificationEnable(this)) { if (PreferencesUtil.isClearKeyboardNotificationEnable(this)) {
if (notificationTimeoutMilliSecs != TimeoutHelper.NEVER) { if (mNotificationTimeoutMilliSecs != TimeoutHelper.NEVER) {
cleanNotificationTimerTask = Thread { defineTimerJob(builder, mNotificationTimeoutMilliSecs) {
val maxPos = 100 stopNotificationAndSendLockIfNeeded()
val posDurationMills = notificationTimeoutMilliSecs / maxPos
for (pos in maxPos downTo 0) {
builder.setProgress(maxPos, pos, false)
notificationManager?.notify(notificationId, builder.build())
try {
Thread.sleep(posDurationMills)
} catch (e: InterruptedException) {
break
}
if (pos <= 0) {
stopNotificationAndSendLockIfNeeded()
}
}
} }
cleanNotificationTimerTask?.start()
} }
} }
} }
@@ -142,8 +126,6 @@ class KeyboardEntryNotificationService : LockNotificationService() {
// Remove the entry from the keyboard // Remove the entry from the keyboard
MagikIME.removeEntry(this) MagikIME.removeEntry(this)
stopTask(cleanNotificationTimerTask)
cleanNotificationTimerTask = null
pendingDeleteIntent?.cancel() pendingDeleteIntent?.cancel()
super.onDestroy() super.onDestroy()

View File

@@ -50,11 +50,6 @@ abstract class LockNotificationService : NotificationService() {
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
} }
protected fun stopTask(task: Thread?) {
if (task != null && task.isAlive)
task.interrupt()
}
override fun onTaskRemoved(rootIntent: Intent?) { override fun onTaskRemoved(rootIntent: Intent?) {
notificationManager?.cancel(notificationId) notificationManager?.cancel(notificationId)

View File

@@ -11,6 +11,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.Stylish import com.kunzisoft.keepass.activities.stylish.Stylish
import kotlinx.coroutines.*
abstract class NotificationService : Service() { abstract class NotificationService : Service() {
@@ -18,6 +19,8 @@ abstract class NotificationService : Service() {
protected var notificationManager: NotificationManagerCompat? = null protected var notificationManager: NotificationManagerCompat? = null
private var colorNotificationAccent: Int = 0 private var colorNotificationAccent: Int = 0
protected var mTimerJob: Job? = null
protected abstract val notificationId: Int protected abstract val notificationId: Int
override fun onBind(intent: Intent): IBinder? { override fun onBind(intent: Intent): IBinder? {
@@ -71,7 +74,33 @@ abstract class NotificationService : Service() {
} }
} }
protected fun defineTimerJob(builder: NotificationCompat.Builder,
timeoutMilliseconds: Long,
actionAfterASecond: (() -> Unit)? = null,
actionEnd: () -> Unit) {
mTimerJob?.cancel()
mTimerJob = CoroutineScope(Dispatchers.Main).launch {
val timeoutInSeconds = timeoutMilliseconds / 1000L
for (currentTime in timeoutInSeconds downTo 0) {
actionAfterASecond?.invoke()
builder.setProgress(100,
(currentTime * 100 / timeoutInSeconds).toInt(),
false)
startForeground(notificationId, builder.build())
delay(1000)
if (currentTime <= 0) {
actionEnd()
}
}
notificationManager?.cancel(notificationId)
mTimerJob = null
cancel()
}
}
override fun onDestroy() { override fun onDestroy() {
mTimerJob?.cancel()
mTimerJob = null
notificationManager?.cancel(notificationId) notificationManager?.cancel(notificationId)
super.onDestroy() super.onDestroy()

View File

@@ -20,9 +20,12 @@
package com.kunzisoft.keepass.settings package com.kunzisoft.keepass.settings
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.settings.preferencedialogfragment.DurationDialogFragmentCompat
class MagikeyboardSettingsFragment : PreferenceFragmentCompat() { class MagikeyboardSettingsFragment : PreferenceFragmentCompat() {
@@ -30,4 +33,31 @@ class MagikeyboardSettingsFragment : PreferenceFragmentCompat() {
// Load the preferences from an XML resource // Load the preferences from an XML resource
setPreferencesFromResource(R.xml.preferences_keyboard, rootKey) setPreferencesFromResource(R.xml.preferences_keyboard, rootKey)
} }
override fun onDisplayPreferenceDialog(preference: Preference?) {
var otherDialogFragment = false
var dialogFragment: DialogFragment? = null
// Main Preferences
when (preference?.key) {
getString(R.string.keyboard_entry_timeout_key) -> {
dialogFragment = DurationDialogFragmentCompat.newInstance(preference.key)
}
else -> otherDialogFragment = true
}
if (dialogFragment != null) {
dialogFragment.setTargetFragment(this, 0)
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
}
// Could not be handled here. Try with the super method.
else if (otherDialogFragment) {
super.onDisplayPreferenceDialog(preference)
}
}
companion object {
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
}
} }

View File

@@ -30,6 +30,7 @@ import android.view.autofill.AutofillManager
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
@@ -46,6 +47,7 @@ import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.icons.IconPackChooser import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.services.AdvancedUnlockNotificationService import com.kunzisoft.keepass.services.AdvancedUnlockNotificationService
import com.kunzisoft.keepass.settings.preference.IconPackListPreference import com.kunzisoft.keepass.settings.preference.IconPackListPreference
import com.kunzisoft.keepass.settings.preferencedialogfragment.DurationDialogFragmentCompat
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.utils.UriUtil
@@ -90,6 +92,20 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
} }
true true
} }
findPreference<Preference>(getString(R.string.import_app_properties_key))?.setOnPreferenceClickListener { _ ->
(activity as? SettingsActivity?)?.apply {
importAppProperties()
}
true
}
findPreference<Preference>(getString(R.string.export_app_properties_key))?.setOnPreferenceClickListener { _ ->
(activity as? SettingsActivity?)?.apply {
exportAppProperties()
}
true
}
} }
} }
@@ -388,10 +404,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
Stylish.assignStyle(activity, styleIdString) Stylish.assignStyle(activity, styleIdString)
// Relaunch the current activity to redraw theme // Relaunch the current activity to redraw theme
(activity as? SettingsActivity?)?.apply { (activity as? SettingsActivity?)?.apply {
keepCurrentScreen() relaunchCurrentScreen()
startActivity(intent)
finish()
activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
} }
} }
styleEnabled styleEnabled
@@ -399,10 +412,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
findPreference<ListPreference>(getString(R.string.setting_style_brightness_key))?.setOnPreferenceChangeListener { _, _ -> findPreference<ListPreference>(getString(R.string.setting_style_brightness_key))?.setOnPreferenceChangeListener { _, _ ->
(activity as? SettingsActivity?)?.apply { (activity as? SettingsActivity?)?.apply {
keepCurrentScreen() relaunchCurrentScreen()
startActivity(intent)
finish()
activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
} }
true true
} }
@@ -440,6 +450,31 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
} }
} }
override fun onDisplayPreferenceDialog(preference: Preference?) {
var otherDialogFragment = false
var dialogFragment: DialogFragment? = null
// Main Preferences
when (preference?.key) {
getString(R.string.app_timeout_key),
getString(R.string.clipboard_timeout_key),
getString(R.string.temp_advanced_unlock_timeout_key) -> {
dialogFragment = DurationDialogFragmentCompat.newInstance(preference.key)
}
else -> otherDialogFragment = true
}
if (dialogFragment != null) {
dialogFragment.setTargetFragment(this, 0)
dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
}
// Could not be handled here. Try with the super method.
else if (otherDialogFragment) {
super.onDisplayPreferenceDialog(preference)
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
activity?.let { activity -> activity?.let { activity ->
@@ -470,7 +505,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
} }
companion object { companion object {
private const val REQUEST_CODE_AUTOFILL = 5201 private const val REQUEST_CODE_AUTOFILL = 5201
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
} }
} }

View File

@@ -576,7 +576,6 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
} }
companion object { companion object {
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT" private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
} }
} }

View File

@@ -21,14 +21,17 @@ package com.kunzisoft.keepass.settings
import android.app.backup.BackupManager import android.app.backup.BackupManager
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.content.res.Resources import android.content.res.Resources
import android.net.Uri import android.net.Uri
import android.util.Log
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.Stylish import com.kunzisoft.keepass.activities.stylish.Stylish
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
import com.kunzisoft.keepass.database.element.SortNodeEnum import com.kunzisoft.keepass.database.element.SortNodeEnum
import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
import java.util.* import java.util.*
@@ -134,28 +137,46 @@ object PreferencesUtil {
} }
fun getStyle(context: Context): String { fun getStyle(context: Context): String {
val stylishPrefKey = context.getString(R.string.setting_style_key) val defaultStyleString = Stylish.defaultStyle(context)
val defaultStyleString = context.getString(R.string.list_style_name_light)
val styleString = PreferenceManager.getDefaultSharedPreferences(context) val styleString = PreferenceManager.getDefaultSharedPreferences(context)
.getString(stylishPrefKey, defaultStyleString) .getString(context.getString(R.string.setting_style_key), defaultStyleString)
?: defaultStyleString ?: defaultStyleString
return Stylish.retrieveEquivalentLightStyle(context, styleString) // Return the system style
return Stylish.retrieveEquivalentSystemStyle(context, styleString)
}
fun setStyle(context: Context, styleString: String) {
var tempThemeString = styleString
if (tempThemeString in BuildConfig.STYLES_DISABLED) {
tempThemeString = Stylish.defaultStyle(context)
}
// Store light style to show selection in array list
tempThemeString = Stylish.retrieveEquivalentLightStyle(context, tempThemeString)
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putString(context.getString(R.string.setting_style_key), tempThemeString)
.apply()
Stylish.load(context)
} }
fun getStyleBrightness(context: Context): String? { fun getStyleBrightness(context: Context): String? {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(context.getString(R.string.setting_style_brightness_key), return prefs.getString(context.getString(R.string.setting_style_brightness_key),
context.resources.getString(R.string.list_style_brightness_follow_system)) context.getString(R.string.list_style_brightness_follow_system))
} }
/** /**
* Retrieve the text size in % (1 for 100%) * Retrieve the text size in % (1 for 100%)
*/ */
fun getListTextSize(context: Context): Float { fun getListTextSize(context: Context): Float {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val index = try {
val listSizeString = prefs.getString(context.getString(R.string.list_size_key), val prefs = PreferenceManager.getDefaultSharedPreferences(context)
context.getString(R.string.list_size_string_medium)) val listSizeString = prefs.getString(context.getString(R.string.list_size_key),
val index = context.resources.getStringArray(R.array.list_size_string_values).indexOf(listSizeString) context.getString(R.string.list_size_string_medium))
context.resources.getStringArray(R.array.list_size_string_values).indexOf(listSizeString)
} catch (e: Exception) {
1
}
val typedArray = context.resources.obtainTypedArray(R.array.list_size_values) val typedArray = context.resources.obtainTypedArray(R.array.list_size_values)
val listSize = typedArray.getFloat(index, 1.0F) val listSize = typedArray.getFloat(index, 1.0F)
typedArray.recycle() typedArray.recycle()
@@ -289,11 +310,13 @@ object PreferencesUtil {
} }
fun getListSort(context: Context): SortNodeEnum { fun getListSort(context: Context): SortNodeEnum {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) try {
prefs.getString(context.getString(R.string.sort_node_key), val prefs = PreferenceManager.getDefaultSharedPreferences(context)
SortNodeEnum.DB.name)?.let { prefs.getString(context.getString(R.string.sort_node_key),
return SortNodeEnum.valueOf(it) SortNodeEnum.DB.name)?.let {
} return SortNodeEnum.valueOf(it)
}
} catch (e: Exception) {}
return SortNodeEnum.DB return SortNodeEnum.DB
} }
@@ -517,4 +540,133 @@ object PreferencesUtil {
.putStringSet(context.getString(R.string.autofill_web_domain_blocklist_key), setItems) .putStringSet(context.getString(R.string.autofill_web_domain_blocklist_key), setItems)
.apply() .apply()
} }
fun getAppProperties(context: Context): Properties {
val properties = Properties()
for ((name, value) in PreferenceManager.getDefaultSharedPreferences(context).all) {
properties[name] = value.toString()
}
for ((name, value) in Education.getEducationSharedPreferences(context).all) {
properties[name] = value.toString()
}
return properties
}
private fun getStringSetFromProperties(value: String): Set<String> {
return value.removePrefix("[")
.removeSuffix("]")
.split(", ")
.toSet()
}
private fun putPropertiesInPreferences(properties: Properties,
preferences: SharedPreferences,
putProperty: (editor: SharedPreferences.Editor,
name: String,
value: String) -> Unit) {
preferences.edit().apply {
for ((name, value) in properties) {
try {
putProperty(this, name as String, value as String)
} catch (e:Exception) {
Log.e("PreferencesUtil", "Error when trying to parse app property $name=$value", e)
}
}
}.apply()
}
fun setAppProperties(context: Context, properties: Properties) {
putPropertiesInPreferences(properties,
PreferenceManager.getDefaultSharedPreferences(context)) { editor, name, value ->
when (name) {
context.getString(R.string.allow_no_password_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.delete_entered_password_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.enable_read_only_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.enable_auto_save_database_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.omit_backup_search_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.auto_focus_search_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.subdomain_search_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.app_timeout_key) -> editor.putString(name, value.toLong().toString())
context.getString(R.string.lock_database_screen_off_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.lock_database_back_root_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.lock_database_show_button_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.password_length_key) -> editor.putInt(name, value.toInt())
context.getString(R.string.list_password_generator_options_key) -> editor.putStringSet(name, getStringSetFromProperties(value))
context.getString(R.string.hide_password_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.allow_copy_password_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.remember_database_locations_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.show_recent_files_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.hide_broken_locations_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.remember_keyfile_locations_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.biometric_unlock_enable_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.device_credential_unlock_enable_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.biometric_auto_open_prompt_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.temp_advanced_unlock_enable_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.temp_advanced_unlock_timeout_key) -> editor.putString(name, value.toLong().toString())
context.getString(R.string.magic_keyboard_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.clipboard_notifications_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.clear_clipboard_notification_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.clipboard_timeout_key) -> editor.putString(name, value.toLong().toString())
context.getString(R.string.settings_autofill_enable_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_notification_entry_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_notification_entry_clear_close_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_entry_timeout_key) -> editor.putString(name, value.toLong().toString())
context.getString(R.string.keyboard_selection_entry_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_search_share_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_save_search_info_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_auto_go_action_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_key_vibrate_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_key_sound_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_previous_database_credentials_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_previous_fill_in_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_previous_lock_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_close_database_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_auto_search_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_inline_suggestions_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_save_search_info_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_ask_to_save_data_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_application_id_blocklist_key) -> editor.putStringSet(name, getStringSetFromProperties(value))
context.getString(R.string.autofill_web_domain_blocklist_key) -> editor.putStringSet(name, getStringSetFromProperties(value))
context.getString(R.string.setting_style_key) -> setStyle(context, value)
context.getString(R.string.setting_style_brightness_key) -> editor.putString(name, value)
context.getString(R.string.setting_icon_pack_choose_key) -> editor.putString(name, value)
context.getString(R.string.list_entries_show_username_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.list_groups_show_number_entries_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.list_size_key) -> editor.putString(name, value)
context.getString(R.string.monospace_font_fields_enable_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.hide_expired_entries_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.show_uuid_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.enable_education_screens_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.sort_node_key) -> editor.putString(name, value)
context.getString(R.string.sort_group_before_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.sort_ascending_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.sort_recycle_bin_bottom_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.allow_copy_password_first_time_key) -> editor.putBoolean(name, value.toBoolean())
}
}
putPropertiesInPreferences(properties,
Education.getEducationSharedPreferences(context)) { editor, name, value ->
when (name) {
context.getString(R.string.education_create_db_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_select_db_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_unlock_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_read_only_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_biometric_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_search_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_new_node_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_sort_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_lock_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_copy_username_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_entry_edit_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_password_generator_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_entry_new_field_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_add_attachment_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.education_setup_OTP_key) -> editor.putBoolean(name, value.toBoolean())
}
}
}
} }

View File

@@ -24,22 +24,27 @@ import android.app.backup.BackupManager
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
import com.kunzisoft.keepass.activities.stylish.Stylish
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.model.MainCredential import com.kunzisoft.keepass.model.MainCredential
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.view.showActionErrorIfNeeded import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import java.util.*
open class SettingsActivity open class SettingsActivity
: LockingActivity(), : LockingActivity(),
@@ -48,6 +53,8 @@ open class SettingsActivity
PasswordEncodingDialogFragment.Listener { PasswordEncodingDialogFragment.Listener {
private var backupManager: BackupManager? = null private var backupManager: BackupManager? = null
private var mExternalFileHelper: ExternalFileHelper? = null
private var appPropertiesFileCreationRequestCode: Int? = null
private var coordinatorLayout: CoordinatorLayout? = null private var coordinatorLayout: CoordinatorLayout? = null
private var toolbar: Toolbar? = null private var toolbar: Toolbar? = null
@@ -70,6 +77,8 @@ open class SettingsActivity
coordinatorLayout = findViewById(R.id.toolbar_coordinator) coordinatorLayout = findViewById(R.id.toolbar_coordinator)
toolbar = findViewById(R.id.toolbar) toolbar = findViewById(R.id.toolbar)
mExternalFileHelper = ExternalFileHelper(this)
if (savedInstanceState?.getString(TITLE_KEY).isNullOrEmpty()) if (savedInstanceState?.getString(TITLE_KEY).isNullOrEmpty())
toolbar?.setTitle(R.string.settings) toolbar?.setTitle(R.string.settings)
else else
@@ -216,6 +225,13 @@ open class SettingsActivity
hideOrShowLockButton(key) hideOrShowLockButton(key)
} }
fun relaunchCurrentScreen() {
keepCurrentScreen()
startActivity(intent)
finish()
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
}
/** /**
* To keep the current screen when activity is reloaded * To keep the current screen when activity is reloaded
*/ */
@@ -235,6 +251,58 @@ open class SettingsActivity
replaceFragment(key, reload) replaceFragment(key, reload)
} }
fun importAppProperties() {
mExternalFileHelper?.openDocument()
}
fun exportAppProperties() {
appPropertiesFileCreationRequestCode = mExternalFileHelper?.createDocument(getString(R.string.app_properties_file_name))
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Import app properties result
try {
mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { selectedfileUri ->
selectedfileUri?.let { uri ->
val appProperties = Properties()
contentResolver?.openInputStream(uri)?.use { inputStream ->
appProperties.load(inputStream)
}
PreferencesUtil.setAppProperties(this, appProperties)
// Restart the current activity
relaunchCurrentScreen()
Toast.makeText(this, R.string.success_import_app_properties, Toast.LENGTH_LONG).show()
}
}
} catch (e: Exception) {
Toast.makeText(this, R.string.error_import_app_properties, Toast.LENGTH_LONG).show()
Log.e(TAG, "Unable to import app properties", e)
}
// Export app properties result
try {
if (requestCode == appPropertiesFileCreationRequestCode) {
mExternalFileHelper?.onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri ->
createdFileUri?.let { uri ->
contentResolver?.openOutputStream(uri)?.use { outputStream ->
PreferencesUtil
.getAppProperties(this)
.store(outputStream, getString(R.string.description_app_properties))
}
Toast.makeText(this, R.string.success_export_app_properties, Toast.LENGTH_LONG).show()
}
}
appPropertiesFileCreationRequestCode = null
}
} catch (e: Exception) {
Toast.makeText(this, R.string.error_export_app_properties, Toast.LENGTH_LONG).show()
Log.e(LockingActivity.TAG, "Unable to export app properties", e)
}
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
@@ -244,6 +312,8 @@ open class SettingsActivity
companion object { companion object {
private val TAG = SettingsActivity::class.java.name
private const val SHOW_LOCK = "SHOW_LOCK" private const val SHOW_LOCK = "SHOW_LOCK"
private const val TITLE_KEY = "TITLE_KEY" private const val TITLE_KEY = "TITLE_KEY"
private const val TAG_NESTED = "TAG_NESTED" private const val TAG_NESTED = "TAG_NESTED"

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2019 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 <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.settings.preference
import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import androidx.preference.DialogPreference
import com.kunzisoft.keepass.R
class DurationDialogPreference @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.dialogPreferenceStyle,
defStyleRes: Int = defStyleAttr)
: DialogPreference(context, attrs, defStyleAttr, defStyleRes) {
private var mDuration: Long = 0L
override fun getDialogLayoutResource(): Int {
return R.layout.pref_dialog_duration
}
/**
* Get current duration of preference
*/
fun getDuration(): Long {
return if (mDuration >= 0) mDuration else -1
}
/**
* Assign [duration] of preference
*/
fun setDuration(duration: Long) {
persistString(duration.toString())
notifyChanged()
}
override fun onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any?) {
if (restorePersistedValue) {
mDuration = getPersistedString(mDuration.toString()).toLongOrNull() ?: mDuration
} else {
mDuration = defaultValue?.toString()?.toLongOrNull() ?: mDuration
persistString(mDuration.toString())
}
}
override fun onGetDefaultValue(a: TypedArray?, index: Int): Any {
return try {
a?.getString(index)?.toLongOrNull() ?: mDuration
} catch (e: Exception) {
mDuration
}
}
// Was previously a string
override fun persistString(value: String?): Boolean {
mDuration = value?.toLongOrNull() ?: mDuration
return super.persistString(value)
}
}

View File

@@ -32,7 +32,7 @@ class InputKdfSizePreference @JvmOverloads constructor(context: Context,
override fun setSummary(summary: CharSequence) { override fun setSummary(summary: CharSequence) {
if (summary == UNKNOWN_VALUE_STRING) { if (summary == UNKNOWN_VALUE_STRING) {
super.setSummary("") super.setSummary(summary)
} else { } else {
var summaryString = summary var summaryString = summary
try { try {

View File

@@ -0,0 +1,180 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.settings.preferencedialogfragment
import android.os.Bundle
import android.view.View
import android.widget.NumberPicker
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.settings.preference.DurationDialogPreference
class DurationDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
private var mEnabled = true
private var mDays = 0
private var mHours = 0
private var mMinutes = 0
private var mSeconds = 0
private var daysNumberPicker: NumberPicker? = null
private var hoursNumberPicker: NumberPicker? = null
private var minutesNumberPicker: NumberPicker? = null
private var secondsNumberPicker: NumberPicker? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// To get items from saved instance state
if (savedInstanceState != null
&& savedInstanceState.containsKey(ENABLE_KEY)
&& savedInstanceState.containsKey(DAYS_KEY)
&& savedInstanceState.containsKey(HOURS_KEY)
&& savedInstanceState.containsKey(MINUTES_KEY)
&& savedInstanceState.containsKey(SECONDS_KEY)) {
mEnabled = savedInstanceState.getBoolean(ENABLE_KEY)
mDays = savedInstanceState.getInt(DAYS_KEY)
mHours = savedInstanceState.getInt(HOURS_KEY)
mMinutes = savedInstanceState.getInt(MINUTES_KEY)
mSeconds = savedInstanceState.getInt(SECONDS_KEY)
} else {
val currentPreference = preference
if (currentPreference is DurationDialogPreference) {
durationToDaysHoursMinutesSeconds(currentPreference.getDuration())
}
}
}
private fun durationToDaysHoursMinutesSeconds(duration: Long) {
if (duration < 0) {
mDays = 0
mHours = 0
mMinutes = 0
mSeconds = 0
} else {
mDays = (duration / (24L * 60L * 60L * 1000L)).toInt()
val daysMilliseconds = mDays * 24L * 60L * 60L * 1000L
mHours = ((duration - daysMilliseconds) / (60L * 60L * 1000L)).toInt()
val hoursMilliseconds = mHours * 60L * 60L * 1000L
mMinutes = ((duration - daysMilliseconds - hoursMilliseconds) / (60L * 1000L)).toInt()
val minutesMilliseconds = mMinutes * 60L * 1000L
mSeconds = ((duration - daysMilliseconds - hoursMilliseconds - minutesMilliseconds) / (1000L)).toInt()
}
}
private fun assignValuesInViews() {
daysNumberPicker?.value = mDays
hoursNumberPicker?.value = mHours
minutesNumberPicker?.value = mMinutes
secondsNumberPicker?.value = mSeconds
}
override fun onBindDialogView(view: View) {
super.onBindDialogView(view)
daysNumberPicker = view.findViewById<NumberPicker>(R.id.days_picker).apply {
minValue = 0
maxValue = 364
setOnValueChangedListener { _, _, newVal ->
mDays = newVal
activateSwitch()
}
}
hoursNumberPicker = view.findViewById<NumberPicker>(R.id.hours_picker).apply {
minValue = 0
maxValue = 23
setOnValueChangedListener { _, _, newVal ->
mHours = newVal
activateSwitch()
}
}
minutesNumberPicker = view.findViewById<NumberPicker>(R.id.minutes_picker).apply {
minValue = 0
maxValue = 59
setOnValueChangedListener { _, _, newVal ->
mMinutes = newVal
activateSwitch()
}
}
secondsNumberPicker = view.findViewById<NumberPicker>(R.id.seconds_picker).apply {
minValue = 0
maxValue = 59
setOnValueChangedListener { _, _, newVal ->
mSeconds = newVal
activateSwitch()
}
}
setSwitchAction({ isChecked ->
mEnabled = isChecked
}, mDays + mHours + mMinutes + mSeconds > 0)
assignValuesInViews()
}
private fun buildDuration(): Long {
return if (mEnabled) {
mDays * 24L * 60L * 60L * 1000L +
mHours * 60L * 60L * 1000L +
mMinutes * 60L * 1000L +
mSeconds * 1000L
} else {
-1
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(ENABLE_KEY, mEnabled)
outState.putInt(DAYS_KEY, mDays)
outState.putInt(HOURS_KEY, mHours)
outState.putInt(MINUTES_KEY, mMinutes)
outState.putInt(SECONDS_KEY, mSeconds)
}
override fun onDialogClosed(positiveResult: Boolean) {
if (positiveResult) {
val currentPreference = preference
if (currentPreference is DurationDialogPreference) {
currentPreference.setDuration(buildDuration())
}
}
}
companion object {
private const val ENABLE_KEY = "ENABLE_KEY"
private const val DAYS_KEY = "DAYS_KEY"
private const val HOURS_KEY = "HOURS_KEY"
private const val MINUTES_KEY = "MINUTES_KEY"
private const val SECONDS_KEY = "SECONDS_KEY"
fun newInstance(key: String): DurationDialogFragmentCompat {
val fragment = DurationDialogFragmentCompat()
val bundle = Bundle(1)
bundle.putString(ARG_KEY, key)
fragment.arguments = bundle
return fragment
}
}
}

View File

@@ -154,4 +154,14 @@ abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCom
onCheckedChange?.invoke(isChecked) onCheckedChange?.invoke(isChecked)
} }
} }
fun activateSwitch() {
if (switchElementView?.isChecked != true)
switchElementView?.isChecked = true
}
fun deactivateSwitch() {
if (switchElementView?.isChecked == true)
switchElementView?.isChecked = false
}
} }

View File

@@ -67,7 +67,7 @@ class ClipboardHelper(private val context: Context) {
fun getClipboard(context: Context): CharSequence { fun getClipboard(context: Context): CharSequence {
if (getClipboardManager()?.hasPrimaryClip() == true) { if (getClipboardManager()?.hasPrimaryClip() == true) {
val data = getClipboardManager()?.primaryClip val data = getClipboardManager()?.primaryClip
if (data!!.itemCount > 0) { if (data != null && data.itemCount > 0) {
val text = data.getItemAt(0).coerceToText(context) val text = data.getItemAt(0).coerceToText(context)
if (text != null) { if (text != null) {
return text return text

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2019 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 <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.utils
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment
private var CREATE_FILE_REQUEST_CODE_DEFAULT = 3853
private var fileRequestCodes = ArrayList<Int>()
fun getUnusedCreateFileRequestCode(): Int {
val newCreateFileRequestCode = CREATE_FILE_REQUEST_CODE_DEFAULT++
fileRequestCodes.add(newCreateFileRequestCode)
return newCreateFileRequestCode
}
@SuppressLint("InlinedApi")
fun allowCreateDocumentByStorageAccessFramework(packageManager: PackageManager): Boolean {
return when {
// To check if a custom file manager can manage the ACTION_CREATE_DOCUMENT
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> {
packageManager.queryIntentActivities(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/x-keepass"
}, PackageManager.MATCH_DEFAULT_ONLY).isNotEmpty()
}
else -> true
}
}
fun createDocument(activity: FragmentActivity,
titleString: String,
typeString: String = "application/octet-stream"): Int? {
val idCode = getUnusedCreateFileRequestCode()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
activity.startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = typeString
putExtra(Intent.EXTRA_TITLE, titleString)
}, idCode)
return idCode
} catch (e: Exception) {
FileManagerDialogFragment().show(activity.supportFragmentManager, "browserDialog")
}
} else {
FileManagerDialogFragment().show(activity.supportFragmentManager, "browserDialog")
}
return null
}
fun onCreateDocumentResult(requestCode: Int, resultCode: Int, data: Intent?,
action: (fileCreated: Uri?)->Unit) {
// Retrieve the created URI from the file manager
if (fileRequestCodes.contains(requestCode) && resultCode == Activity.RESULT_OK) {
action.invoke(data?.data)
fileRequestCodes.remove(requestCode)
}
}

View File

@@ -38,16 +38,26 @@ object UriUtil {
fun getFileData(context: Context, fileUri: Uri?): DocumentFile? { fun getFileData(context: Context, fileUri: Uri?): DocumentFile? {
if (fileUri == null) if (fileUri == null)
return null return null
return when { return try {
isFileScheme(fileUri) -> { when {
fileUri.path?.let { isFileScheme(fileUri) -> {
File(it).let { file -> fileUri.path?.let {
return DocumentFile.fromFile(file) File(it).let { file ->
return DocumentFile.fromFile(file)
}
} }
} }
isContentScheme(fileUri) -> {
DocumentFile.fromSingleUri(context, fileUri)
}
else -> {
Log.e("FileData", "Content scheme not known")
null
}
} }
isContentScheme(fileUri) -> DocumentFile.fromSingleUri(context, fileUri) } catch (e: Exception) {
else -> null Log.e("FileData", "Unable to get document file", e)
null
} }
} }

View File

@@ -6,9 +6,9 @@ import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.documentfile.provider.DocumentFile
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
class KeyFileSelectionView @JvmOverloads constructor(context: Context, class KeyFileSelectionView @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
@@ -51,7 +51,7 @@ class KeyFileSelectionView @JvmOverloads constructor(context: Context,
set(value) { set(value) {
mUri = value mUri = value
keyFileNameView.text = value?.let { keyFileNameView.text = value?.let {
DocumentFile.fromSingleUri(context, value)?.name ?: value.path UriUtil.getFileData(context, value)?.name ?: value.path
} ?: "" } ?: ""
} }
} }

View File

@@ -50,14 +50,18 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
).execute() ).execute()
} }
private fun getDatabaseFilesLoadedValue(): DatabaseFileData {
var newValue = databaseFilesLoaded.value
if (newValue == null) {
newValue = DatabaseFileData()
}
return newValue
}
fun loadListOfDatabases() { fun loadListOfDatabases() {
checkDefaultDatabase() checkDefaultDatabase()
mFileDatabaseHistoryAction?.getDatabaseFileList { databaseFileListRetrieved -> mFileDatabaseHistoryAction?.getDatabaseFileList { databaseFileListRetrieved ->
var newValue = databaseFilesLoaded.value databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
if (newValue == null) {
newValue = DatabaseFileData()
}
newValue.apply {
databaseFileAction = DatabaseFileAction.NONE databaseFileAction = DatabaseFileAction.NONE
databaseFileToActivate = null databaseFileToActivate = null
databaseFileList.apply { databaseFileList.apply {
@@ -65,14 +69,13 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
addAll(databaseFileListRetrieved) addAll(databaseFileListRetrieved)
} }
} }
databaseFilesLoaded.value = newValue
} }
} }
fun addDatabaseFile(databaseUri: Uri, keyFileUri: Uri?) { fun addDatabaseFile(databaseUri: Uri, keyFileUri: Uri?) {
mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(databaseUri, keyFileUri) { databaseFileAdded -> mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(databaseUri, keyFileUri) { databaseFileAdded ->
databaseFileAdded?.let { _ -> databaseFileAdded?.let { _ ->
databaseFilesLoaded.value = databaseFilesLoaded.value?.apply { databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
this.databaseFileAction = DatabaseFileAction.ADD this.databaseFileAction = DatabaseFileAction.ADD
this.databaseFileList.add(databaseFileAdded) this.databaseFileList.add(databaseFileAdded)
this.databaseFileToActivate = databaseFileAdded this.databaseFileToActivate = databaseFileAdded
@@ -84,7 +87,7 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
fun updateDatabaseFile(databaseFileToUpdate: DatabaseFile) { fun updateDatabaseFile(databaseFileToUpdate: DatabaseFile) {
mFileDatabaseHistoryAction?.addOrUpdateDatabaseFile(databaseFileToUpdate) { databaseFileUpdated -> mFileDatabaseHistoryAction?.addOrUpdateDatabaseFile(databaseFileToUpdate) { databaseFileUpdated ->
databaseFileUpdated?.let { _ -> databaseFileUpdated?.let { _ ->
databaseFilesLoaded.value = databaseFilesLoaded.value?.apply { databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
this.databaseFileAction = DatabaseFileAction.UPDATE this.databaseFileAction = DatabaseFileAction.UPDATE
this.databaseFileList this.databaseFileList
.find { it.databaseUri == databaseFileUpdated.databaseUri } .find { it.databaseUri == databaseFileUpdated.databaseUri }
@@ -104,7 +107,7 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
fun deleteDatabaseFile(databaseFileToDelete: DatabaseFile) { fun deleteDatabaseFile(databaseFileToDelete: DatabaseFile) {
mFileDatabaseHistoryAction?.deleteDatabaseFile(databaseFileToDelete) { databaseFileDeleted -> mFileDatabaseHistoryAction?.deleteDatabaseFile(databaseFileToDelete) { databaseFileDeleted ->
databaseFileDeleted?.let { _ -> databaseFileDeleted?.let { _ ->
databaseFilesLoaded.value = databaseFilesLoaded.value?.apply { databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
databaseFileAction = DatabaseFileAction.DELETE databaseFileAction = DatabaseFileAction.DELETE
databaseFileToActivate = databaseFileDeleted databaseFileToActivate = databaseFileDeleted
databaseFileList.remove(databaseFileDeleted) databaseFileList.remove(databaseFileDeleted)

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,8h14v11zM7,10h5v5L7,15z"/>
</vector>

View File

@@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 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 <http://www.gnu.org/licenses/>.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/edit"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAutofill="noExcludeDescendants"
tools:targetApi="o">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/explanation_text"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:gravity="center"
android:layout_marginBottom="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
style="@style/KeepassDXStyle.TextAppearance.SmallTitle"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_element"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enable"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/explanation_text" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/switch_element">
<LinearLayout
android:id="@+id/duration_days_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toStartOf="@+id/duration_hours_picker"
app:layout_constraintRight_toLeftOf="@+id/duration_hours_picker">
<NumberPicker
android:id="@+id/days_picker"
android:scrollbarFadeDuration="0"
android:scrollbarDefaultDelayBeforeFade="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:src="@drawable/ic_day_white_24dp"
app:tint="?android:attr/textColor"
android:contentDescription="@string/digits" />
</LinearLayout>
<LinearLayout
android:id="@+id/duration_hours_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="HardcodedText"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/duration_days_picker"
app:layout_constraintLeft_toRightOf="@+id/duration_days_picker"
app:layout_constraintEnd_toStartOf="@+id/duration_time_picker"
app:layout_constraintRight_toLeftOf="@+id/duration_time_picker">
<NumberPicker
android:id="@+id/hours_picker"
android:scrollbarFadeDuration="0"
android:scrollbarDefaultDelayBeforeFade="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textStyle="bold"
android:text=":" />
</LinearLayout>
<LinearLayout
android:id="@+id/duration_time_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="HardcodedText"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@+id/duration_hours_picker"
app:layout_constraintLeft_toRightOf="@+id/duration_hours_picker"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent">
<NumberPicker
android:id="@+id/minutes_picker"
android:scrollbarFadeDuration="0"
android:scrollbarDefaultDelayBeforeFade="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="28sp"
android:text="'"/>
<NumberPicker
android:id="@+id/seconds_picker"
android:scrollbarFadeDuration="0"
android:scrollbarDefaultDelayBeforeFade="0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="28sp"
android:text="''"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -19,4 +19,7 @@
<string name="add_entry">এন্টরি যোগ করুন</string> <string name="add_entry">এন্টরি যোগ করুন</string>
<string name="accept">গ্রহণ</string> <string name="accept">গ্রহণ</string>
<string name="about_description">কিপাস পাসওয়ার্ড ম্যানেজারের অ্যান্ড্রয়েড বাস্তবায়ন</string> <string name="about_description">কিপাস পাসওয়ার্ড ম্যানেজারের অ্যান্ড্রয়েড বাস্তবায়ন</string>
<string name="clipboard_error_title">ক্লিপবোর্ড ত্রুটি</string>
<string name="allow">অনুমোদন</string>
<string name="file_manager_install_description">ACTION_CREATE_DOCUMENT এবং ACTION_OPEN_DOCUMENT অভিপ্রায় গ্রহণ করে এমন একটি ফাইল ম্যানেজার ডাটাবেস ফাইলগুলো তৈরি করা, খোলা এবং সংরক্ষণ করতে প্রয়োজন।</string>
</resources> </resources>

View File

@@ -19,21 +19,21 @@
Catalan translation by Oriol Garrote Catalan translation by Oriol Garrote
--><resources> --><resources>
<string name="feedback">Comentaris</string> <string name="feedback">Comentaris</string>
<string name="homepage">Pàgina inici:</string> <string name="homepage">Pàgina d\'inici</string>
<string name="about_description">KeePassDX és una implementació per a Android de KeePass password manager</string> <string name="about_description">KeePassDX és una implementació per a Android de KeePass password manager</string>
<string name="accept">Accepta</string> <string name="accept">Accepta</string>
<string name="add_entry">Afegeix entrada</string> <string name="add_entry">Afegeix entrada</string>
<string name="add_group">Afegeix grup</string> <string name="add_group">Afegeix grup</string>
<string name="encryption_algorithm">Algorisme de xifratge</string> <string name="encryption_algorithm">Algoritme de xifrat</string>
<string name="app_timeout">Temps d\'espera de l\'aplicació</string> <string name="app_timeout">Temps d\'espera de l\'aplicació</string>
<string name="app_timeout_summary">Temps d\'inactivitat abans de blocar la base de dades</string> <string name="app_timeout_summary">Temps d\'inactivitat abans de blocar la base de dades</string>
<string name="application">Aplicació</string> <string name="application">Aplicació</string>
<string name="menu_app_settings">Paràmetres de l\'aplicació</string> <string name="menu_app_settings">Configuració de l\'aplicació</string>
<string name="brackets">Parèntesis</string> <string name="brackets">Parèntesis</string>
<string name="file_manager_install_description">L\'exploració d\'arxius necessita l\'aplicació Open Intents File Manager, clica a sota per instal·lar-la. Degut a peculiaritats de l\'explorador d\'arxius pot ser que no funcioni correctament la primera execució.</string> <string name="file_manager_install_description">L\'exploració d\'arxius necessita l\'aplicació Open Intents File Manager, clica a sota per instal·lar-la. Degut a peculiaritats de l\'explorador d\'arxius pot ser que no funcioni correctament la primera execució.</string>
<string name="clipboard_cleared">Porta-retalls netejat.</string> <string name="clipboard_cleared">Porta-retalls netejat.</string>
<string name="clipboard_timeout">Temps d\'espera del porta-retalls</string> <string name="clipboard_timeout">Temps d\'espera del porta-retalls</string>
<string name="clipboard_timeout_summary">Temps abans de netejar el porta-retalls</string> <string name="clipboard_timeout_summary">Temps abans de netejar el porta-retalls (si el teu dispositiu ho suporta)</string>
<string name="select_to_copy">Selecciona per copiar %1$s al porta-retalls</string> <string name="select_to_copy">Selecciona per copiar %1$s al porta-retalls</string>
<string name="retrieving_db_key">Creant clau de base de dades…</string> <string name="retrieving_db_key">Creant clau de base de dades…</string>
<string name="database">Base de dades</string> <string name="database">Base de dades</string>
@@ -115,24 +115,13 @@
<string name="search_label">Cerca</string> <string name="search_label">Cerca</string>
<string name="sort_db">Ordre natural</string> <string name="sort_db">Ordre natural</string>
<string name="special">Especial</string> <string name="special">Especial</string>
<string name="search">Títol/descripció d\'entrada</string> <string name="search">Cercar</string>
<string name="search_results">Resultats de cerca</string> <string name="search_results">Resultats de cerca</string>
<string name="underline">Subratllat</string> <string name="underline">Subratllat</string>
<string name="unsupported_db_version">Versió de la base de dades no suportada.</string> <string name="unsupported_db_version">Versió de la base de dades no suportada.</string>
<string name="uppercase">Majúscules</string> <string name="uppercase">Majúscules</string>
<string name="version_label">Versió %1$s</string> <string name="version_label">Versió %1$s</string>
<string name="education_unlock_summary">Introdueix una contrasenya i/o un arxiu clau per desbloquejar la base de dades.</string> <string name="education_unlock_summary">Introdueix una contrasenya i/o un arxiu clau per desbloquejar la base de dades.</string>
<string-array name="timeout_options">
<item>5 segons</item>
<item>10 segons</item>
<item>20 segons</item>
<item>30 segons</item>
<item>1 minut</item>
<item>5 minuts</item>
<item>15 minuts</item>
<item>30 minuts</item>
<item>Mai</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Petita</item> <item>Petita</item>
<item>Mitjana</item> <item>Mitjana</item>
@@ -144,7 +133,7 @@
<string name="edit_entry">Edita l\'entrada</string> <string name="edit_entry">Edita l\'entrada</string>
<string name="contribution">Contribució</string> <string name="contribution">Contribució</string>
<string name="contact">Contacte</string> <string name="contact">Contacte</string>
<string name="extended_ASCII">ASCII ampliat</string> <string name="extended_ASCII">ASCII estès</string>
<string name="sort_username">Nom d\'usuari</string> <string name="sort_username">Nom d\'usuari</string>
<string name="sort_title">Títol</string> <string name="sort_title">Títol</string>
<string name="sort_menu">Ordena</string> <string name="sort_menu">Ordena</string>
@@ -199,8 +188,8 @@
<string name="sort_last_access_time">Accés</string> <string name="sort_last_access_time">Accés</string>
<string name="sort_last_modify_time">Modificació</string> <string name="sort_last_modify_time">Modificació</string>
<string name="sort_creation_time">Creació</string> <string name="sort_creation_time">Creació</string>
<string name="sort_recycle_bin_bottom">Paperera a baix</string> <string name="sort_recycle_bin_bottom">La paperera de reciclatge es troba a la part inferior</string>
<string name="sort_groups_before">Primer els grups</string> <string name="sort_groups_before">Grups primer</string>
<string name="sort_ascending">El menor primer ↓</string> <string name="sort_ascending">El menor primer ↓</string>
<string name="command_execution">S\'executa l\'ordre…</string> <string name="command_execution">S\'executa l\'ordre…</string>
<string name="parallelism_explanation">Grau de paral·lelisme (és a dir, nombre de fils) fets servir per la funció de derivació de la clau.</string> <string name="parallelism_explanation">Grau de paral·lelisme (és a dir, nombre de fils) fets servir per la funció de derivació de la clau.</string>
@@ -228,21 +217,21 @@
<string name="menu_open_file_read_and_write">Modificable</string> <string name="menu_open_file_read_and_write">Modificable</string>
<string name="menu_file_selection_read_only">Protegit contra escriptura</string> <string name="menu_file_selection_read_only">Protegit contra escriptura</string>
<string name="menu_save_database">Desa la base de dades</string> <string name="menu_save_database">Desa la base de dades</string>
<string name="menu_cancel">Cancel·la</string> <string name="menu_cancel">Cancel·lar</string>
<string name="menu_paste">Enganxa</string> <string name="menu_paste">Enganxar</string>
<string name="menu_move">Mou</string> <string name="menu_move">Moure</string>
<string name="menu_copy">Copia</string> <string name="menu_copy">Copiar</string>
<string name="menu_master_key_settings">Paràmetres de la contrasenya mestra</string> <string name="menu_master_key_settings">Paràmetres de la contrasenya mestra</string>
<string name="menu_security_settings">Paràmetres de seguretat</string> <string name="menu_security_settings">Paràmetres de seguretat</string>
<string name="menu_advanced_unlock_settings">Desblocatge avançat</string> <string name="menu_advanced_unlock_settings">Desblocatge avançat</string>
<string name="menu_form_filling_settings">Reompliment de formularis</string> <string name="menu_form_filling_settings">Emplenat de formularis</string>
<string name="copy_field">Còpia de %1$s</string> <string name="copy_field">Còpia de %1$s</string>
<string name="creating_database">Es crea la base de dades…</string> <string name="creating_database">Es crea la base de dades…</string>
<string name="list_groups_show_number_entries_summary">Mostra el nombre d\'entrades en un grup</string> <string name="list_groups_show_number_entries_summary">Mostra el nombre d\'entrades en un grup</string>
<string name="list_groups_show_number_entries_title">Mostra el nombre d\'entades</string> <string name="list_groups_show_number_entries_title">Mostra el nombre d\'entades</string>
<string name="list_entries_show_username_summary">Mostra els noms d\'usuari en les llistes d\'entrades</string> <string name="list_entries_show_username_summary">Mostra els noms d\'usuari en les llistes d\'entrades</string>
<string name="list_entries_show_username_title">Mostra noms d\'usuari</string> <string name="list_entries_show_username_title">Mostra noms d\'usuari</string>
<string name="keyfile_is_empty">El fitxer de claus és buit.</string> <string name="keyfile_is_empty">El fitxer de clau és buit.</string>
<string name="invalid_algorithm">Algorisme incorrecte.</string> <string name="invalid_algorithm">Algorisme incorrecte.</string>
<string name="file_not_found_content">No s\'ha trobat el fitxer. Mireu de reobrir-lo des de l\'explorador de fitxers.</string> <string name="file_not_found_content">No s\'ha trobat el fitxer. Mireu de reobrir-lo des de l\'explorador de fitxers.</string>
<string name="field_value">Valor del camp</string> <string name="field_value">Valor del camp</string>
@@ -290,5 +279,15 @@
<string name="content_description_background">Fons</string> <string name="content_description_background">Fons</string>
<string name="clipboard_error_clear">No ha estat possible netejar el porta-retalls</string> <string name="clipboard_error_clear">No ha estat possible netejar el porta-retalls</string>
<string name="clipboard_error">Alguns dispositius no permeten que les aplicacions facin servir el porta-retalls.</string> <string name="clipboard_error">Alguns dispositius no permeten que les aplicacions facin servir el porta-retalls.</string>
<string name="clipboard_error_title">Error del porta-retalls</string> <string name="clipboard_error_title">Error del Porta Retalls</string>
<string name="error_string_key">Cada cadena ha de tenir un nom de camp.</string>
<string name="error_rebuild_list">La llista no s\'ha pogut reconstruir correctament.</string>
<string name="error_database_uri_null">No es pot recuperar l\'URI de la base de dades.</string>
<string name="error_field_name_already_exists">El nom del camp ja existeix.</string>
<string name="error_registration_read_only">No es permet desar un element nou en una base de dades de només lectura</string>
<string name="error_string_type">Aquest text no coincideix amb l\'element sol·licitat.</string>
<string name="error_otp_type">L\'OTP existent no està reconegut per aquest formulari, la seva validació ja no pot generar correctament el token.</string>
<string name="error_create_database_file">No s\'ha pogut crear una base de dades amb aquesta contrasenya i arxiu de clau.</string>
<string name="error_move_folder_in_itself">No pots moure un grup dintre d\'ell mateix.</string>
<string name="error_autofill_enable_service">No s\'ha pogut habilitar el servei d\'autocompletat.</string>
</resources> </resources>

View File

@@ -39,7 +39,7 @@
<string name="select_to_copy">Vyberte zkopírovat %1$s do schránky</string> <string name="select_to_copy">Vyberte zkopírovat %1$s do schránky</string>
<string name="retrieving_db_key">Načítám klíč databáze…</string> <string name="retrieving_db_key">Načítám klíč databáze…</string>
<string name="database">Databáze</string> <string name="database">Databáze</string>
<string name="decrypting_db">Dešifruji obsah databáze…</string> <string name="decrypting_db">Dešifrování obsahu databáze…</string>
<string name="default_checkbox">Použít jako výchozí databázi</string> <string name="default_checkbox">Použít jako výchozí databázi</string>
<string name="digits">Číslice</string> <string name="digits">Číslice</string>
<string name="select_database_file">Otevřít existující databázi</string> <string name="select_database_file">Otevřít existující databázi</string>
@@ -88,7 +88,7 @@
<string name="length">Délka</string> <string name="length">Délka</string>
<string name="list_size_title">Velikost položek seznamu</string> <string name="list_size_title">Velikost položek seznamu</string>
<string name="list_size_summary">Velikost textu v seznamu prvků</string> <string name="list_size_summary">Velikost textu v seznamu prvků</string>
<string name="loading_database">Načítám databázi</string> <string name="loading_database">Načítá databáze</string>
<string name="lowercase">Malá písmena</string> <string name="lowercase">Malá písmena</string>
<string name="hide_password_title">Skrýt hesla</string> <string name="hide_password_title">Skrýt hesla</string>
<string name="hide_password_summary">Ve výchozím stavu zobrazit (***) místo hesla</string> <string name="hide_password_summary">Ve výchozím stavu zobrazit (***) místo hesla</string>
@@ -111,7 +111,7 @@
<string name="no_url_handler">Pro otevření tohoto URL nainstalujte webový prohlížeč.</string> <string name="no_url_handler">Pro otevření tohoto URL nainstalujte webový prohlížeč.</string>
<string name="omit_backup_search_title">Neprohledávat položky v záloze</string> <string name="omit_backup_search_title">Neprohledávat položky v záloze</string>
<string name="omit_backup_search_summary">Vynechat skupiny „Záloha“ a \"Koš\" z výsledků vyhledávání</string> <string name="omit_backup_search_summary">Vynechat skupiny „Záloha“ a \"Koš\" z výsledků vyhledávání</string>
<string name="progress_create">Zakládám novou databázi</string> <string name="progress_create">Zakládá nové databáze</string>
<string name="progress_title">Pracuji…</string> <string name="progress_title">Pracuji…</string>
<string name="protection">Ochrana</string> <string name="protection">Ochrana</string>
<string name="read_only_warning">Ke změně v databáze potřebuje KeePassDX oprávnění pro zápis.</string> <string name="read_only_warning">Ke změně v databáze potřebuje KeePassDX oprávnění pro zápis.</string>
@@ -119,8 +119,8 @@
<string name="root">Kořen</string> <string name="root">Kořen</string>
<string name="rounds">Transformační průchody</string> <string name="rounds">Transformační průchody</string>
<string name="rounds_explanation">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í.</string> <string name="rounds_explanation">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í.</string>
<string name="saving_database">Ukládám databázi</string> <string name="saving_database">Ukládá databáze</string>
<string name="space">Místo</string> <string name="space">Mezera</string>
<string name="search_label">Hledat</string> <string name="search_label">Hledat</string>
<string name="sort_db">Přirozené řazení</string> <string name="sort_db">Přirozené řazení</string>
<string name="special">Speciální</string> <string name="special">Speciální</string>
@@ -133,17 +133,6 @@
<string name="education_unlock_summary">Databázi odemknete zadáním hesla a/nebo souboru s klíčem. <string name="education_unlock_summary">Databázi odemknete zadáním hesla a/nebo souboru s klíčem.
\n \n
\nNezapomeňte po každé úpravě zálohovat kopii svého .kdbx souboru na bezpečné místo.</string> \nNezapomeňte po každé úpravě zálohovat kopii svého .kdbx souboru na bezpečné místo.</string>
<string-array name="timeout_options">
<item>5 sekund</item>
<item>10 sekund</item>
<item>20 sekund</item>
<item>30 sekund</item>
<item>1 minuta</item>
<item>5 minut</item>
<item>15 minut</item>
<item>30 minut</item>
<item>Nikdy</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Malý</item> <item>Malý</item>
<item>Střední</item> <item>Střední</item>
@@ -374,7 +363,7 @@
<string name="error_otp_period">Interval musít být mezi %1$d a %2$d vteřinami.</string> <string name="error_otp_period">Interval musít být mezi %1$d a %2$d vteřinami.</string>
<string name="error_otp_digits">Token musí obsahovat mezi %1$d a %2$d číslicemi.</string> <string name="error_otp_digits">Token musí obsahovat mezi %1$d a %2$d číslicemi.</string>
<string name="invalid_db_same_uuid">%1$s s totožným UUID %2$s již existuje.</string> <string name="invalid_db_same_uuid">%1$s s totožným UUID %2$s již existuje.</string>
<string name="creating_database">Zakládám databázi</string> <string name="creating_database">Zakládá databáze</string>
<string name="menu_security_settings">Nastavení zabezpečení</string> <string name="menu_security_settings">Nastavení zabezpečení</string>
<string name="menu_master_key_settings">Nastavení hlavního klíče</string> <string name="menu_master_key_settings">Nastavení hlavního klíče</string>
<string name="contains_duplicate_uuid">Databáze obsahuje duplikátní UUID.</string> <string name="contains_duplicate_uuid">Databáze obsahuje duplikátní UUID.</string>
@@ -403,11 +392,11 @@
<string name="error_save_database">Nebylo možno uložit databázi.</string> <string name="error_save_database">Nebylo možno uložit databázi.</string>
<string name="menu_save_database">Uložit databázi</string> <string name="menu_save_database">Uložit databázi</string>
<string name="menu_empty_recycle_bin">Vysypat koš</string> <string name="menu_empty_recycle_bin">Vysypat koš</string>
<string name="command_execution">Provádím příkaz…</string> <string name="command_execution">Provádění příkazu</string>
<string name="warning_permanently_delete_nodes">Natrvalo smazat vybrané uzly\?</string> <string name="warning_permanently_delete_nodes">Natrvalo smazat vybrané uzly\?</string>
<string name="keystore_not_accessible">Úložiště klíčů není řádně inicializováno.</string> <string name="keystore_not_accessible">Úložiště klíčů není řádně inicializováno.</string>
<string name="recycle_bin_group_title">Název skupiny</string> <string name="recycle_bin_group_title">Název skupiny</string>
<string name="enable_auto_save_database_title">Uložit databázi automaticky</string> <string name="enable_auto_save_database_title">Automatické ukládání</string>
<string name="enable_auto_save_database_summary">Uložit databázi po každé důležité akci (v režimu \"Zápis\")</string> <string name="enable_auto_save_database_summary">Uložit databázi po každé důležité akci (v režimu \"Zápis\")</string>
<string name="entry_attachments">Přílohy</string> <string name="entry_attachments">Přílohy</string>
<string name="menu_restore_entry_history">Obnovit historii</string> <string name="menu_restore_entry_history">Obnovit historii</string>
@@ -415,9 +404,9 @@
<string name="keyboard_auto_go_action_title">Akce auto-klávesy</string> <string name="keyboard_auto_go_action_title">Akce auto-klávesy</string>
<string name="keyboard_auto_go_action_summary">Akce klávesy \"Jít\" po stisknutí klávesy \"Kolonka\"</string> <string name="keyboard_auto_go_action_summary">Akce klávesy \"Jít\" po stisknutí klávesy \"Kolonka\"</string>
<string name="download_attachment">Stáhnout %1$s</string> <string name="download_attachment">Stáhnout %1$s</string>
<string name="download_initialization">Zahajuji</string> <string name="download_initialization">Zahájení</string>
<string name="download_progression">Probíhá: %1$d%%</string> <string name="download_progression">Probíhá: %1$d%%</string>
<string name="download_finalization">Dokončuji</string> <string name="download_finalization">Dokončování</string>
<string name="download_complete">Kompletní!</string> <string name="download_complete">Kompletní!</string>
<string name="hide_expired_entries_title">Skrýt propadlé záznamy</string> <string name="hide_expired_entries_title">Skrýt propadlé záznamy</string>
<string name="hide_expired_entries_summary">Propadlé záznamy nebudou ukázány</string> <string name="hide_expired_entries_summary">Propadlé záznamy nebudou ukázány</string>
@@ -437,7 +426,7 @@
<string name="warning_database_read_only">Udělit právo zápisu pro uložení změn v databázi</string> <string name="warning_database_read_only">Udělit právo zápisu pro uložení změn v databázi</string>
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft je <strong>open source</strong> a <strong>bez reklam</strong>. <string name="html_about_licence">KeePassDX © %1$d Kunzisoft je <strong>open source</strong> a <strong>bez reklam</strong>.
\nJe poskytován jak je, od licencí <strong>GPLv3</strong>, bez jakékoli záruky.</string> \nJe poskytován jak je, od licencí <strong>GPLv3</strong>, bez jakékoli záruky.</string>
<string name="html_about_contribution">Abychom si &lt;strong&gt;udrželi svoji svobodu&lt;/strong&gt;, &lt;strong&gt;opravili chyby&lt;/strong&gt;,&lt;strong&gt;doplnili funkce&lt;/strong&gt; a &lt;strong&gt;byli vždy aktivní&lt;/strong&gt;, počítáme s Vaším &lt;strong&gt;přispěním&lt;/strong&gt;.</string> <string name="html_about_contribution">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>.</string>
<string name="error_create_database">Nepodařilo se vytvořit soubor databáze.</string> <string name="error_create_database">Nepodařilo se vytvořit soubor databáze.</string>
<string name="entry_add_attachment">Přidat přílohu</string> <string name="entry_add_attachment">Přidat přílohu</string>
<string name="discard">Zavrhnout</string> <string name="discard">Zavrhnout</string>
@@ -523,11 +512,11 @@
<string name="advanced_unlock_not_recognized">Otisk pro rozšířené odemknutí nebyl rozpoznán</string> <string name="advanced_unlock_not_recognized">Otisk pro rozšířené odemknutí nebyl rozpoznán</string>
<string name="advanced_unlock_invalid_key">Nelze načíst klíč rozšířeného odemknutí. Prosím, smažte jej a opakujte proces rozpoznání odemknutí.</string> <string name="advanced_unlock_invalid_key">Nelze načíst klíč rozšířeného odemknutí. Prosím, smažte jej a opakujte proces rozpoznání odemknutí.</string>
<string name="advanced_unlock_prompt_extract_credential_message">Načíst důvěrný údaj pomocí dat rozšířeného odemknutí</string> <string name="advanced_unlock_prompt_extract_credential_message">Načíst důvěrný údaj pomocí dat rozšířeného odemknutí</string>
<string name="advanced_unlock_prompt_extract_credential_title">Otevřít databázi pomocí rozpoznání rozšířeného odemknutí</string> <string name="advanced_unlock_prompt_extract_credential_title">Otevřít pomocí rozšířeného odemykání</string>
<string name="advanced_unlock_prompt_store_credential_message">Varování: Pokud použijete rozpoznání rozšířeného odemknutí, musíte si i nadále pamatovat hlavní heslo.</string> <string name="advanced_unlock_prompt_store_credential_message">Varování: Pokud použijete rozpoznání rozšířeného odemknutí, musíte si i nadále pamatovat hlavní heslo.</string>
<string name="advanced_unlock_prompt_store_credential_title">Rozpoznání rozšířeného odemknutí</string> <string name="advanced_unlock_prompt_store_credential_title">Rozpoznání rozšířeného odemknutí</string>
<string name="open_advanced_unlock_prompt_store_credential">Pro uložení důvěrných údajů otevřete pobídku rozšířeného odemknutí</string> <string name="open_advanced_unlock_prompt_store_credential">Pro uložení důvěrných údajů otevřete pobídku rozšířeného odemknutí</string>
<string name="open_advanced_unlock_prompt_unlock_database">Pro odemknutí databáze otevřete pobídku rozšířeného odemknutí</string> <string name="open_advanced_unlock_prompt_unlock_database">Databázi otevřete i pomocí nabídky rozšířeného odemykání</string>
<string name="menu_keystore_remove_key">Smazat klíč rozšířeného odemknutí</string> <string name="menu_keystore_remove_key">Smazat klíč rozšířeného odemknutí</string>
<string name="education_advanced_unlock_title">Rozšířené odemknutí databáze</string> <string name="education_advanced_unlock_title">Rozšířené odemknutí databáze</string>
<string name="advanced_unlock_timeout">Časový limit rozšířeného odemknutí</string> <string name="advanced_unlock_timeout">Časový limit rozšířeného odemknutí</string>

View File

@@ -132,17 +132,6 @@
<string name="education_unlock_summary">Angiv en adgangskode og/eller en nøglefil til at låse databasen op. <string name="education_unlock_summary">Angiv en adgangskode og/eller en nøglefil til at låse databasen op.
\n \n
\nHusk at gemme en kopi af .kdbx filen i et sikkert sted efter hver ændring.</string> \nHusk at gemme en kopi af .kdbx filen i et sikkert sted efter hver ændring.</string>
<string-array name="timeout_options">
<item>5 sekunder</item>
<item>10 sekunder</item>
<item>20 sekunder</item>
<item>30 sekunder</item>
<item>1 minut</item>
<item>5 minutter</item>
<item>15 minutter</item>
<item>30 minutter</item>
<item>Aldrig</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Lille</item> <item>Lille</item>
<item>Mellem</item> <item>Mellem</item>
@@ -508,4 +497,38 @@
<string name="autofill_save_search_info_summary">Forsøger at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg</string> <string name="autofill_save_search_info_summary">Forsøger at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg</string>
<string name="keyboard_save_search_info_summary">Forsøger at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg</string> <string name="keyboard_save_search_info_summary">Forsøger at gemme delte oplysninger, når der foretages et manuelt indtastningsvalg</string>
<string name="error_field_name_already_exists">Feltnavnet findes allerede.</string> <string name="error_field_name_already_exists">Feltnavnet findes allerede.</string>
<string name="icon_section_custom">Brugerdefineret</string>
<string name="icon_section_standard">Standard</string>
<string name="style_brightness_summary">Vælg lys eller mørk tema</string>
<string name="style_brightness_title">Temalysstyrke</string>
<string name="unit_gibibyte">GiB</string>
<string name="unit_mebibyte">MiB</string>
<string name="unit_kibibyte">KiB</string>
<string name="unit_byte">B</string>
<string name="download_canceled">Annulleret!</string>
<string name="education_advanced_unlock_title">Avanceret database-oplåsning</string>
<string name="autofill_inline_suggestions_keyboard">Forslag til autofyld tilføjet.</string>
<string name="select_entry">Vælg post</string>
<string name="back_to_previous_keyboard">Tilbage til forrige tastatur</string>
<string name="custom_fields">Brugerdefinerede felter</string>
<string name="advanced_unlock_delete_all_key_warning">Slet alle krypteringsnøgler relateret til avanceret oplåsningsgenkendelse\?</string>
<string name="advanced_unlock_tap_delete">Tryk for at slette avancerede oplåsningstaster</string>
<string name="content">Indhold</string>
<string name="credential_before_click_advanced_unlock_button">Indtast adgangskoden, og klik derefter på denne knap.</string>
<string name="advanced_unlock_prompt_not_initialized">Kunne ikke initialisere avanceret oplåsningsprompt.</string>
<string name="advanced_unlock_scanning_error">Fejl ved avanceret oplåsning: %1$s</string>
<string name="advanced_unlock_not_recognized">Kunne ikke genkende avanceret oplåsning</string>
<string name="advanced_unlock_invalid_key">Den avancerede oplåsningsnøgle kan ikke læses. Slet den, og gentag proceduren for genkendelse af oplåsning.</string>
<string name="advanced_unlock_prompt_extract_credential_title">Åbn database med avanceret oplåsningsgenkendelse</string>
<string name="advanced_unlock_prompt_store_credential_title">Avanceret oplåsningsgenkendelse</string>
<string name="warning_database_info_changed_options">Overskriv de eksterne ændringer ved at gemme databasen eller genindlæse den med de seneste ændringer.</string>
<string name="warning_database_info_changed">Oplysningerne i databasefilen er blevet ændret uden for appen.</string>
<string name="menu_keystore_remove_key">Slet avanceret oplåsningsnøgle</string>
<string name="menu_reload_database">Genindlæs database</string>
<string name="error_duplicate_file">Fildataene findes allerede.</string>
<string name="error_upload_file">Der opstod en fejl under overførsel af fildataene.</string>
<string name="error_file_to_big">Filen, du prøver at overføre, er for stor.</string>
<string name="error_rebuild_list">Listen kan ikke genopbygges korrekt.</string>
<string name="error_database_uri_null">Database-URI kan ikke hentes.</string>
<string name="content_description_otp_information">Oplysninger om engangsadgangskode</string>
</resources> </resources>

View File

@@ -147,17 +147,6 @@
<string name="education_unlock_summary">Geben Sie das Passwort und/oder die Schlüsseldatei ein, um Ihre Datenbank zu entsperren. <string name="education_unlock_summary">Geben Sie das Passwort und/oder die Schlüsseldatei ein, um Ihre Datenbank zu entsperren.
\n \n
\nSichern Sie Ihre Datenbankdatei nach jeder Änderung an einem sicheren Ort.</string> \nSichern Sie Ihre Datenbankdatei nach jeder Änderung an einem sicheren Ort.</string>
<string-array name="timeout_options">
<item>5 Sekunden</item>
<item>10 Sekunden</item>
<item>20 Sekunden</item>
<item>30 Sekunden</item>
<item>1 Minute</item>
<item>5 Minuten</item>
<item>15 Minuten</item>
<item>30 Minuten</item>
<item>Nie</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Klein</item> <item>Klein</item>
<item>Mittel</item> <item>Mittel</item>

View File

@@ -135,17 +135,6 @@
<string name="education_unlock_summary">Καταχωρίστε τον κωδικό πρόσβασης και /ή το αρχείο-κλειδί για να ξεκλειδώσετε τη βάση δεδομένων σας. <string name="education_unlock_summary">Καταχωρίστε τον κωδικό πρόσβασης και /ή το αρχείο-κλειδί για να ξεκλειδώσετε τη βάση δεδομένων σας.
\n \n
\nΔημιουργήστε αντίγραφα ασφαλείας του αρχείου βάσης δεδομένων σας, σε ασφαλές μέρος μετά από κάθε αλλαγή.</string> \nΔημιουργήστε αντίγραφα ασφαλείας του αρχείου βάσης δεδομένων σας, σε ασφαλές μέρος μετά από κάθε αλλαγή.</string>
<string-array name="timeout_options">
<item>5 δευτερόλεπτα</item>
<item>10 δευτερόλεπτα</item>
<item>20 δευτερόλεπτα</item>
<item>30 δευτερόλεπτα</item>
<item>1 λεπτό</item>
<item>5 λεπτά</item>
<item>15 λεπτά</item>
<item>30 λεπτά</item>
<item>Ποτέ</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Μικρά</item> <item>Μικρά</item>
<item>Μεσαία</item> <item>Μεσαία</item>
@@ -559,4 +548,6 @@
<string name="error_upload_file">Παρουσιάστηκε σφάλμα κατά τη μεταφόρτωση των δεδομένων αρχείου.</string> <string name="error_upload_file">Παρουσιάστηκε σφάλμα κατά τη μεταφόρτωση των δεδομένων αρχείου.</string>
<string name="error_file_to_big">Το αρχείο που προσπαθείτε να ανεβάσετε είναι πολύ μεγάλο.</string> <string name="error_file_to_big">Το αρχείο που προσπαθείτε να ανεβάσετε είναι πολύ μεγάλο.</string>
<string name="content_description_otp_information">Πληροφορίες One-time κωδικού πρόσβασης</string> <string name="content_description_otp_information">Πληροφορίες One-time κωδικού πρόσβασης</string>
<string name="error_remove_file">Παρουσιάστηκε σφάλμα κατά την κατάργηση των δεδομένων αρχείου.</string>
<string name="error_duplicate_file">Τα δεδομένα αρχείου υπάρχουν ήδη.</string>
</resources> </resources>

View File

@@ -125,17 +125,6 @@
<string name="education_unlock_summary">Introduzca la contraseña y/o el archivo clave para desbloquear su base de datos. <string name="education_unlock_summary">Introduzca la contraseña y/o el archivo clave para desbloquear su base de datos.
\n \n
\nHaga una copia de seguridad de su archivo de base de datos en un lugar seguro después de cada cambio.</string> \nHaga una copia de seguridad de su archivo de base de datos en un lugar seguro después de cada cambio.</string>
<string-array name="timeout_options">
<item>5 segundos</item>
<item>10 segundos</item>
<item>20 segundos</item>
<item>30 segundos</item>
<item>1 minuto</item>
<item>5 minutos</item>
<item>15 minutos</item>
<item>30 minutos</item>
<item>Nunca</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Pequeño</item> <item>Pequeño</item>
<item>Mediano</item> <item>Mediano</item>
@@ -470,7 +459,7 @@
<string name="content">Contenido</string> <string name="content">Contenido</string>
<string name="clipboard_explanation_summary">Copiar los campos de entrada usando el portapapeles de su dispositivo</string> <string name="clipboard_explanation_summary">Copiar los campos de entrada usando el portapapeles de su dispositivo</string>
<string name="device_credential">Credenciales del dispositivo</string> <string name="device_credential">Credenciales del dispositivo</string>
<string name="credential_before_click_advanced_unlock_button">Introduzca la contraseña y luego haga clic en el botón \"Desbloqueo avanzado\".</string> <string name="credential_before_click_advanced_unlock_button">Introduzca la contraseña y luego haga clic en este botón.</string>
<string name="advanced_unlock_prompt_not_initialized">No se pudo inicializar el indicador de desbloqueo avanzado.</string> <string name="advanced_unlock_prompt_not_initialized">No se pudo inicializar el indicador de desbloqueo avanzado.</string>
<string name="advanced_unlock_scanning_error">Error de desbloqueo avanzado: %1$s</string> <string name="advanced_unlock_scanning_error">Error de desbloqueo avanzado: %1$s</string>
<string name="advanced_unlock_not_recognized">No se pudo reconocer la impresión de desbloqueo avanzado</string> <string name="advanced_unlock_not_recognized">No se pudo reconocer la impresión de desbloqueo avanzado</string>
@@ -554,4 +543,6 @@
<string name="menu_reload_database">Recargar la base de datos</string> <string name="menu_reload_database">Recargar la base de datos</string>
<string name="error_otp_type">El tipo de OTP existente no es reconocido por este formulario, su validación ya no puede generar correctamente el token.</string> <string name="error_otp_type">El tipo de OTP existente no es reconocido por este formulario, su validación ya no puede generar correctamente el token.</string>
<string name="download_canceled">¡Cancelado!</string> <string name="download_canceled">¡Cancelado!</string>
<string name="error_duplicate_file">Los datos de archivo ya existen.</string>
<string name="error_upload_file">Ha habido un error al subir el archivo de datos.</string>
</resources> </resources>

View File

@@ -132,17 +132,6 @@
<string name="uppercase">Maiuskulak</string> <string name="uppercase">Maiuskulak</string>
<string name="version_label">Bertsioa %1$s</string> <string name="version_label">Bertsioa %1$s</string>
<string name="education_unlock_summary">Sartu pasahitz eta / edo gako fitxategi bat zure datubasea desblokeatzeko.</string> <string name="education_unlock_summary">Sartu pasahitz eta / edo gako fitxategi bat zure datubasea desblokeatzeko.</string>
<string-array name="timeout_options">
<item>5 segundu</item>
<item>10 segundu</item>
<item>20 segundu</item>
<item>30 segundu</item>
<item>minutu 1</item>
<item>5 minutu</item>
<item>15 minutu</item>
<item>30 minutu</item>
<item>Inoiz ez</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Txikia</item> <item>Txikia</item>
<item>Ertaina</item> <item>Ertaina</item>

View File

@@ -132,17 +132,6 @@
<string name="uppercase">Isot kirjaimet</string> <string name="uppercase">Isot kirjaimet</string>
<string name="version_label">Versio %1$s</string> <string name="version_label">Versio %1$s</string>
<string name="education_unlock_summary">Syötä salasana ja/tai avaintiedosto avataksesi tietokantasi.</string> <string name="education_unlock_summary">Syötä salasana ja/tai avaintiedosto avataksesi tietokantasi.</string>
<string-array name="timeout_options">
<item>5 sekuntia</item>
<item>10 sekuntia</item>
<item>20 sekuntia</item>
<item>30 sekuntia</item>
<item>1 minuutti</item>
<item>5 minuttia</item>
<item>15 minuttia</item>
<item>30 minuttia</item>
<item>Ei koskaan</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Pieni</item> <item>Pieni</item>
<item>Keskikokoinen</item> <item>Keskikokoinen</item>

View File

@@ -258,17 +258,6 @@
<string name="html_text_dev_feature_upgrade">Noubliez pas de garder votre application à jour en installant les nouvelles versions.</string> <string name="html_text_dev_feature_upgrade">Noubliez pas de garder votre application à jour en installant les nouvelles versions.</string>
<string name="download">Télécharger</string> <string name="download">Télécharger</string>
<string name="contribute">Contribuer</string> <string name="contribute">Contribuer</string>
<string-array name="timeout_options">
<item>5 secondes</item>
<item>10 secondes</item>
<item>20 secondes</item>
<item>30 secondes</item>
<item>1 minute</item>
<item>5 minutes</item>
<item>15 minutes</item>
<item>30 minutes</item>
<item>Jamais</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Petit</item> <item>Petit</item>
<item>Moyen</item> <item>Moyen</item>
@@ -519,7 +508,7 @@
<string name="device_credential_unlock_enable_summary">Vous permet d\'utiliser les informations d\'identification de votre appareil pour ouvrir la base de données</string> <string name="device_credential_unlock_enable_summary">Vous permet d\'utiliser les informations d\'identification de votre appareil pour ouvrir la base de données</string>
<string name="device_credential_unlock_enable_title">Déverrouillage par identifiants de l\'appareil</string> <string name="device_credential_unlock_enable_title">Déverrouillage par identifiants de l\'appareil</string>
<string name="device_credential">Déverouillage de l\'appareil</string> <string name="device_credential">Déverouillage de l\'appareil</string>
<string name="credential_before_click_advanced_unlock_button">Tapez le mot de passe, puis cliquez sur le bouton \"Déverrouillage avancé\".</string> <string name="credential_before_click_advanced_unlock_button">Tapez le mot de passe, puis cliquez sur ce bouton.</string>
<string name="advanced_unlock_prompt_not_initialized">Impossible d\'initialiser l\'invite de déverrouillage avancé.</string> <string name="advanced_unlock_prompt_not_initialized">Impossible d\'initialiser l\'invite de déverrouillage avancé.</string>
<string name="advanced_unlock_scanning_error">Erreur de déverrouillage avancé : %1$s</string> <string name="advanced_unlock_scanning_error">Erreur de déverrouillage avancé : %1$s</string>
<string name="advanced_unlock_not_recognized">Impossible de reconnaître l\'empreinte de déverrouillage avancé</string> <string name="advanced_unlock_not_recognized">Impossible de reconnaître l\'empreinte de déverrouillage avancé</string>
@@ -560,4 +549,13 @@
<string name="unit_byte">Octets</string> <string name="unit_byte">Octets</string>
<string name="error_otp_type">Le type OTP existant n\'est pas reconnu par ce formulaire, sa validation peut ne plus générer correctement le jeton.</string> <string name="error_otp_type">Le type OTP existant n\'est pas reconnu par ce formulaire, sa validation peut ne plus générer correctement le jeton.</string>
<string name="download_canceled">Annulé!</string> <string name="download_canceled">Annulé!</string>
<string name="icon_section_custom">Customisé</string>
<string name="icon_section_standard">Standard</string>
<string name="style_brightness_summary">Sélectionnez des thèmes clairs ou foncés</string>
<string name="style_brightness_title">Luminosité de thème</string>
<string name="error_remove_file">Une erreur s\'est produite lors de la suppression des données du fichier.</string>
<string name="error_duplicate_file">Les données du fichier existent déjà.</string>
<string name="error_upload_file">Une erreur s\'est produite lors du téléchargement des données du fichier.</string>
<string name="error_file_to_big">Le fichier que vous essayez de téléverser est trop gros.</string>
<string name="content_description_otp_information">Information sur le mot de passe à usage unique</string>
</resources> </resources>

View File

@@ -543,4 +543,6 @@
<string name="error_upload_file">Tijekom prijenosa podataka datoteke došlo je do greške.</string> <string name="error_upload_file">Tijekom prijenosa podataka datoteke došlo je do greške.</string>
<string name="error_file_to_big">Datoteka koju pokušavaš prenijeti je prevelika.</string> <string name="error_file_to_big">Datoteka koju pokušavaš prenijeti je prevelika.</string>
<string name="content_description_otp_information">Podaci jednokratne lozinke</string> <string name="content_description_otp_information">Podaci jednokratne lozinke</string>
<string name="error_remove_file">Tijekom uklanjanja podataka datoteke došlo je do greške.</string>
<string name="error_duplicate_file">Podaci datoteke već postoje.</string>
</resources> </resources>

View File

@@ -140,17 +140,6 @@
<string name="education_unlock_summary">Adja meg a jelszót és/vagy a kulcsfájlt, hogy kinyithassa az adatbázist. <string name="education_unlock_summary">Adja meg a jelszót és/vagy a kulcsfájlt, hogy kinyithassa az adatbázist.
\n \n
\nKészítsen biztonsági mentést az adatbázisról minden egyes módosítás után.</string> \nKészítsen biztonsági mentést az adatbázisról minden egyes módosítás után.</string>
<string-array name="timeout_options">
<item>5 másodperc</item>
<item>10 másodperc</item>
<item>20 másodperc</item>
<item>30 másodperc</item>
<item>1 perc</item>
<item>5 perc</item>
<item>15 perc</item>
<item>30 perc</item>
<item>Soha</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Kicsi</item> <item>Kicsi</item>
<item>Közepes</item> <item>Közepes</item>

View File

@@ -322,4 +322,67 @@
<string name="error_registration_read_only">Menyimpan item baru tidak diperbolehkan dalam database read-only</string> <string name="error_registration_read_only">Menyimpan item baru tidak diperbolehkan dalam database read-only</string>
<string name="error_otp_type">Tipe OTP yang ada tidak dikenali oleh formulir ini, validasinya mungkin tidak lagi menghasilkan token dengan benar.</string> <string name="error_otp_type">Tipe OTP yang ada tidak dikenali oleh formulir ini, validasinya mungkin tidak lagi menghasilkan token dengan benar.</string>
<string name="content_description_credentials_information">Info kredensial</string> <string name="content_description_credentials_information">Info kredensial</string>
<string name="autofill_inline_suggestions_keyboard">Saran pengisian otomatis ditambahkan.</string>
<string name="autofill_block_restart">Mulai ulang aplikasi yang berisi formulir untuk mengaktifkan pemblokiran.</string>
<string name="autofill_block">Blokir pengisian otomatis</string>
<string name="autofill_web_domain_blocklist_summary">Daftar blokir yang mencegah pengisian otomatis domain web</string>
<string name="autofill_web_domain_blocklist_title">Daftar blokir domain web</string>
<string name="autofill_application_id_blocklist_summary">Daftar blokir yang mencegah pengisian otomatis aplikasi</string>
<string name="autofill_application_id_blocklist_title">Daftar blokir aplikasi</string>
<string name="autofill_ask_to_save_data_summary">Meminta untuk menyimpan data saat formulir divalidasi</string>
<string name="autofill_ask_to_save_data_title">Minta untuk menyimpan data</string>
<string name="autofill_save_search_info_summary">Cobalah untuk menyimpan informasi pencarian saat membuat pilihan entri manual</string>
<string name="autofill_save_search_info_title">Simpan info pencarian</string>
<string name="autofill_inline_suggestions_title">Saran sebaris</string>
<string name="autofill_auto_search_summary">Secara otomatis menyarankan hasil pencarian dari domain web atau ID aplikasi</string>
<string name="autofill_auto_search_title">Pencarian otomatis</string>
<string name="autofill_close_database_summary">Menutup database setelah pilihan isi-auto</string>
<string name="autofill_close_database_title">Tutup database</string>
<string name="enter">Enter</string>
<string name="backspace">Hapus</string>
<string name="select_entry">Pilih entri</string>
<string name="back_to_previous_keyboard">Kembali ke papan tik sebelumnya</string>
<string name="custom_fields">Bidang kustom</string>
<string name="keyboard_previous_lock_title">Kunci database</string>
<string name="keyboard_auto_go_action_title">Aksi tombol otomatis</string>
<string name="keyboard_keys_category">Tombol</string>
<string name="keyboard_previous_database_credentials_title">Layar kredensial database</string>
<string name="keyboard_change">Ganti papan tik</string>
<string name="keyboard_key_sound_title">Tombol terdengar</string>
<string name="keyboard_key_vibrate_title">Getar saat menekan tombol</string>
<string name="keyboard_auto_go_action_summary">Tindakan tombol \"Go\" setelah menekan tombol \"Field\"</string>
<string name="keyboard_theme_title">Tema papan tik</string>
<string name="keyboard_appearance_category">Tampilan</string>
<string name="keyboard_notification_entry_content_text">%1$s</string>
<string name="keyboard_notification_entry_content_title">%1$s tersedia di Magikeyboard</string>
<string name="keyboard_notification_entry_content_title_text">Entri</string>
<string name="keyboard_entry_timeout_summary">Waktu habis untuk menghapus entri papan tik</string>
<string name="keyboard_entry_timeout_title">Waktu habis</string>
<string name="keyboard_notification_entry_clear_close_summary">Tutup database saat menutup notifikasi</string>
<string name="keyboard_notification_entry_clear_close_title">Bersihkan saat menutup</string>
<string name="keyboard_search_share_title">Telusuri info yang dibagikan</string>
<string name="keyboard_notification_entry_summary">Tampilkan notifikasi ketika entri tersedia</string>
<string name="keyboard_notification_entry_title">Info notifikasi</string>
<string name="keyboard_selection_entry_summary">Tampilkan bidang input di Magikeyboard saat melihat entri</string>
<string name="keyboard_selection_entry_title">Pilihan entri</string>
<string name="keyboard_entry_category">Entri</string>
<string name="keyboard_setting_label">Pengaturan Magikeyboard</string>
<string name="keyboard_label">Magikeyboard (KeePassDX)</string>
<string name="keyboard_name">Magikeyboard</string>
<string name="device_keyboard_setting_title">Pengaturan papan tik perangkat</string>
<string name="education_add_attachment_title">Tambah lampiran</string>
<string name="html_text_dev_feature_work_hard">Kami bekerja keras untuk merilis fitur ini segera.</string>
<string name="icon_section_custom">Kustom</string>
<string name="icon_section_standard">Standar</string>
<string name="style_brightness_summary">Pilih tema terang atau gelap</string>
<string name="style_brightness_title">Kecerahan tema</string>
<string name="download_attachment">Unduh %1$s</string>
<string name="contribute">Kontribusi</string>
<string name="download">Unduh</string>
<string name="html_text_dev_feature_upgrade">Ingatlah untuk selalu memperbarui aplikasi Anda.</string>
<string name="error_remove_file">Timbul galat saat menghapus data berkas.</string>
<string name="error_duplicate_file">Data berkas sudah ada.</string>
<string name="error_upload_file">Timbul galat saat mengunggah data berkas.</string>
<string name="error_file_to_big">File yang Anda unggah terlalu besar.</string>
<string name="content_description_otp_information">Info sandi satu kali</string>
</resources> </resources>

View File

@@ -142,17 +142,6 @@
<string name="education_unlock_summary">Inserisci la password o il file chiave per sbloccare la base di dati. <string name="education_unlock_summary">Inserisci la password o il file chiave per sbloccare la base di dati.
\n \n
\nEseguire il backup del file del database in un luogo sicuro dopo ogni modifica.</string> \nEseguire il backup del file del database in un luogo sicuro dopo ogni modifica.</string>
<string-array name="timeout_options">
<item>5 secondi</item>
<item>10 secondi</item>
<item>20 secondi</item>
<item>30 secondi</item>
<item>1 minuto</item>
<item>5 minuti</item>
<item>15 minuti</item>
<item>30 minuti</item>
<item>Mai</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Piccolo</item> <item>Piccolo</item>
<item>Medio</item> <item>Medio</item>
@@ -534,7 +523,7 @@
<string name="advanced_unlock_prompt_store_credential_message">Attenzione: dovrai sempre ricordare la password principale anche se usi lo sblocco avanzato.</string> <string name="advanced_unlock_prompt_store_credential_message">Attenzione: dovrai sempre ricordare la password principale anche se usi lo sblocco avanzato.</string>
<string name="advanced_unlock_prompt_store_credential_title">Riconoscimento con sblocco avanzato</string> <string name="advanced_unlock_prompt_store_credential_title">Riconoscimento con sblocco avanzato</string>
<string name="device_credential">Credenziali del dispositivo</string> <string name="device_credential">Credenziali del dispositivo</string>
<string name="credential_before_click_advanced_unlock_button">Inserisci la password, quindi clicca sull\'icona \"Sblocco avanzato\".</string> <string name="credential_before_click_advanced_unlock_button">Inserisci la password, poi clicca questo pulsante.</string>
<string name="advanced_unlock_scanning_error">Errore sblocco avanzato: %1$s</string> <string name="advanced_unlock_scanning_error">Errore sblocco avanzato: %1$s</string>
<string name="advanced_unlock_prompt_extract_credential_title">Apri il database con lo sblocco avanzato</string> <string name="advanced_unlock_prompt_extract_credential_title">Apri il database con lo sblocco avanzato</string>
<string name="open_advanced_unlock_prompt_unlock_database">Autentica con lo sblocco avanzato per sbloccare il database</string> <string name="open_advanced_unlock_prompt_unlock_database">Autentica con lo sblocco avanzato per sbloccare il database</string>
@@ -556,4 +545,12 @@
<string name="unit_kibibyte">KiB</string> <string name="unit_kibibyte">KiB</string>
<string name="unit_byte">B</string> <string name="unit_byte">B</string>
<string name="content_description_otp_information">Info password usa e getta</string> <string name="content_description_otp_information">Info password usa e getta</string>
<string name="icon_section_custom">Personalizzato</string>
<string name="icon_section_standard">Standard</string>
<string name="style_brightness_summary">Scegli un tema scuro o chiaro</string>
<string name="style_brightness_title">Luminosità del tema</string>
<string name="error_remove_file">Si è verificato un errore durante la rimozione del file.</string>
<string name="error_duplicate_file">Il file esiste già.</string>
<string name="error_upload_file">Si è verificato un errore durante il caricamento del file.</string>
<string name="error_file_to_big">Il file che stai cercando di caricare è troppo grande.</string>
</resources> </resources>

View File

@@ -129,17 +129,6 @@
<string name="uppercase">רישית</string> <string name="uppercase">רישית</string>
<string name="version_label">גרסה %1$s</string> <string name="version_label">גרסה %1$s</string>
<string name="education_unlock_summary">הזן סיסמה ו/או קובץ מפתח כדי לפתוח את מסד הנתונים.</string> <string name="education_unlock_summary">הזן סיסמה ו/או קובץ מפתח כדי לפתוח את מסד הנתונים.</string>
<string-array name="timeout_options">
<item>5 שניות</item>
<item>10 שניות</item>
<item>20 שניות</item>
<item>30 שניות</item>
<item>דקה אחת</item>
<item>5 דקות</item>
<item>15 דקות</item>
<item>30 דקות</item>
<item>אף פעם</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>קטן</item> <item>קטן</item>
<item>בינוני</item> <item>בינוני</item>

View File

@@ -57,7 +57,7 @@
<string name="content_description_keyfile_checkbox">キーファイルのチェックボックス</string> <string name="content_description_keyfile_checkbox">キーファイルのチェックボックス</string>
<string name="content_description_repeat_toggle_password_visibility">パスワードの可視性を再び切り替える</string> <string name="content_description_repeat_toggle_password_visibility">パスワードの可視性を再び切り替える</string>
<string name="content_description_entry_icon">エントリーのアイコン</string> <string name="content_description_entry_icon">エントリーのアイコン</string>
<string name="validate">承認</string> <string name="validate">検証</string>
<string name="discard_changes">変更を破棄しますか?</string> <string name="discard_changes">変更を破棄しますか?</string>
<string name="discard">破棄</string> <string name="discard">破棄</string>
<string name="entry_password_generator">パスワード生成機能</string> <string name="entry_password_generator">パスワード生成機能</string>
@@ -80,13 +80,13 @@
<string name="entry_cancel">キャンセル</string> <string name="entry_cancel">キャンセル</string>
<string name="entry_notes">備考</string> <string name="entry_notes">備考</string>
<string name="entry_confpassword">パスワードを確認</string> <string name="entry_confpassword">パスワードを確認</string>
<string name="entry_created">作成日</string> <string name="entry_created">作成日</string>
<string name="entry_expires">有効期限</string> <string name="entry_expires">有効期限</string>
<string name="entry_UUID">UUID</string> <string name="entry_UUID">UUID</string>
<string name="entry_history">履歴</string> <string name="entry_history">履歴</string>
<string name="entry_attachments">添付ファイル</string> <string name="entry_attachments">添付ファイル</string>
<string name="entry_keyfile">キーファイル</string> <string name="entry_keyfile">キーファイル</string>
<string name="entry_modified">変更日</string> <string name="entry_modified">変更日</string>
<string name="entry_not_found">エントリーのデータが見つかりませんでした。</string> <string name="entry_not_found">エントリーのデータが見つかりませんでした。</string>
<string name="entry_password">パスワード</string> <string name="entry_password">パスワード</string>
<string name="save">保存</string> <string name="save">保存</string>
@@ -185,7 +185,7 @@
<string name="menu_open">開く</string> <string name="menu_open">開く</string>
<string name="menu_search">検索</string> <string name="menu_search">検索</string>
<string name="menu_showpass">パスワードを表示</string> <string name="menu_showpass">パスワードを表示</string>
<string name="menu_url">URL に移動</string> <string name="menu_url">URL にアクセス</string>
<string name="menu_file_selection_read_only">書き込み禁止</string> <string name="menu_file_selection_read_only">書き込み禁止</string>
<string name="menu_open_file_read_and_write">変更可能</string> <string name="menu_open_file_read_and_write">変更可能</string>
<string name="menu_empty_recycle_bin">ゴミ箱を空にする</string> <string name="menu_empty_recycle_bin">ゴミ箱を空にする</string>
@@ -222,7 +222,7 @@
<string name="hide_broken_locations_summary">最近使ったデータベースの一覧で、壊れたリンクを非表示にします</string> <string name="hide_broken_locations_summary">最近使ったデータベースの一覧で、壊れたリンクを非表示にします</string>
<string name="root">ルート</string> <string name="root">ルート</string>
<string name="encryption_explanation">すべてのデータで使用するデータベース暗号化アルゴリズムです。</string> <string name="encryption_explanation">すべてのデータで使用するデータベース暗号化アルゴリズムです。</string>
<string name="kdf_explanation">暗号化アルゴリズム用の鍵を生成するために、マスターキーはランダムなソルトを加える鍵導出関数を使用して変換されます。</string> <string name="kdf_explanation">暗号化アルゴリズム用の鍵を生成するために、マスターキーはランダムなソルト付き鍵導出関数を使用して変換されます。</string>
<string name="rounds">変換ラウンド</string> <string name="rounds">変換ラウンド</string>
<string name="rounds_explanation">変換ラウンドを増やすことでブルート フォース攻撃に対する保護が強化されますが、読み込みと保存が本当に遅くなる可能性があります。</string> <string name="rounds_explanation">変換ラウンドを増やすことでブルート フォース攻撃に対する保護が強化されますが、読み込みと保存が本当に遅くなる可能性があります。</string>
<string name="memory_usage">メモリ使用量</string> <string name="memory_usage">メモリ使用量</string>
@@ -470,17 +470,6 @@
<string name="download_progression">進行中:%1$d%%</string> <string name="download_progression">進行中:%1$d%%</string>
<string name="download_finalization">終了しています…</string> <string name="download_finalization">終了しています…</string>
<string name="download_complete">完了しました!</string> <string name="download_complete">完了しました!</string>
<string-array name="timeout_options">
<item>5秒</item>
<item>10秒</item>
<item>20秒</item>
<item>30秒</item>
<item>1分</item>
<item>5分</item>
<item>15分</item>
<item>30分</item>
<item>なし</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item></item> <item></item>
<item></item> <item></item>
@@ -516,7 +505,7 @@
<string name="device_credential_unlock_enable_summary">デバイス認証情報を使用してデータベースを開くことができるようにします</string> <string name="device_credential_unlock_enable_summary">デバイス認証情報を使用してデータベースを開くことができるようにします</string>
<string name="device_credential_unlock_enable_title">デバイス認証情報によるロック解除</string> <string name="device_credential_unlock_enable_title">デバイス認証情報によるロック解除</string>
<string name="device_credential">デバイス認証情報</string> <string name="device_credential">デバイス認証情報</string>
<string name="credential_before_click_advanced_unlock_button">パスワードを入力し、[高度なロック解除] ボタンをタップします。</string> <string name="credential_before_click_advanced_unlock_button">パスワードを入力し、このボタンをタップします。</string>
<string name="advanced_unlock_prompt_not_initialized">高度なロック解除プロンプトを初期化できません。</string> <string name="advanced_unlock_prompt_not_initialized">高度なロック解除プロンプトを初期化できません。</string>
<string name="advanced_unlock_scanning_error">高度なロック解除のエラー:%1$s</string> <string name="advanced_unlock_scanning_error">高度なロック解除のエラー:%1$s</string>
<string name="advanced_unlock_invalid_key">高度なロック解除用の鍵が読み取れません。削除してロック解除の手順をやり直してください。</string> <string name="advanced_unlock_invalid_key">高度なロック解除用の鍵が読み取れません。削除してロック解除の手順をやり直してください。</string>
@@ -549,6 +538,15 @@
<string name="unit_mebibyte">MiB</string> <string name="unit_mebibyte">MiB</string>
<string name="unit_kibibyte">KiB</string> <string name="unit_kibibyte">KiB</string>
<string name="unit_byte">B</string> <string name="unit_byte">B</string>
<string name="download_canceled">キャンセルされました!</string> <string name="download_canceled">キャンセルました</string>
<string name="error_otp_type">既存の OTP 形式がこのフォームで認識されない場合、検証によってトークンが正しく生成されなくなる可能性があります。</string> <string name="error_otp_type">既存の OTP の種類がこのフォームで認識されていないため、フォームの検証によってトークンが正しく生成されなくなる可能性があります。</string>
<string name="icon_section_custom">カスタム</string>
<string name="icon_section_standard">標準</string>
<string name="style_brightness_summary">ライトテーマとダークテーマのどちらかを選択します</string>
<string name="style_brightness_title">テーマの明るさ</string>
<string name="error_remove_file">ファイルデータの削除中にエラーが発生しました。</string>
<string name="error_duplicate_file">ファイルデータはすでに存在します。</string>
<string name="error_upload_file">ファイルデータのアップロード中にエラーが発生しました。</string>
<string name="error_file_to_big">アップロードしようとしているファイルが大きすぎます。</string>
<string name="content_description_otp_information">ワンタイムパスワードについて</string>
</resources> </resources>

View File

@@ -129,17 +129,6 @@
<string name="uppercase">Lielie burti</string> <string name="uppercase">Lielie burti</string>
<string name="version_label">Versija %1$s</string> <string name="version_label">Versija %1$s</string>
<string name="education_unlock_summary">Ievadiet paroli/atslēgas failu, lai atbloķētu savu datu bāzi.</string> <string name="education_unlock_summary">Ievadiet paroli/atslēgas failu, lai atbloķētu savu datu bāzi.</string>
<string-array name="timeout_options">
<item>5 sekundes</item>
<item>10 sekundes</item>
<item>20 sekundes</item>
<item>30 sekundes</item>
<item>1 minūte</item>
<item>5 minūtes</item>
<item>15 minūtes</item>
<item>30 minūtes</item>
<item>Nekad</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Mazs</item> <item>Mazs</item>
<item>Vidējs</item> <item>Vidējs</item>

View File

@@ -453,4 +453,19 @@
<string name="error_string_type">Denne teksten samsvarer ikke med det valgte elementet.</string> <string name="error_string_type">Denne teksten samsvarer ikke med det valgte elementet.</string>
<string name="html_about_contribution">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>.</string> <string name="html_about_contribution">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>.</string>
<string name="content_description_repeat_toggle_password_visibility">Gjenta for å skifte passordsynlighet</string> <string name="content_description_repeat_toggle_password_visibility">Gjenta for å skifte passordsynlighet</string>
<string name="icon_section_custom">Egendefinert</string>
<string name="icon_section_standard">Forvalg</string>
<string name="style_brightness_summary">Velg lys eller mørk drakt</string>
<string name="style_brightness_title">Draktlysstyrke</string>
<string name="backspace">Rettetast</string>
<string name="keyboard_save_search_info_title">Lagre delt info</string>
<string name="keyboard_search_share_title">Søk i delt info</string>
<string name="device_keyboard_setting_title">Innstillinger for enhetens tastatur</string>
<string name="notification">Merknad</string>
<string name="data">Data</string>
<string name="advanced_unlock_tap_delete">Trykk for å slette avanserte opplåsningsnøkler</string>
<string name="clipboard_explanation_summary">Kopier inntastingsfelter ved bruk av utklippstavlen på din enhet</string>
<string name="autofill_preference_title">Autofyllings-innstillinger</string>
<string name="warning_sure_remove_data">Fjern denne dataen uansett\?</string>
<string name="warning_permanently_delete_nodes">Slett valgte noder for godt\?</string>
</resources> </resources>

View File

@@ -125,17 +125,6 @@
<string name="education_unlock_summary">Voer het wachtwoord en/of sleutelbestand in om je database te ontgrendelen. <string name="education_unlock_summary">Voer het wachtwoord en/of sleutelbestand in om je database te ontgrendelen.
\n \n
\nMaak na elke aanpassing een kopie van je .kdbx-bestand op een veilige locatie.</string> \nMaak na elke aanpassing een kopie van je .kdbx-bestand op een veilige locatie.</string>
<string-array name="timeout_options">
<item>5 seconden</item>
<item>10 seconden</item>
<item>20 seconden</item>
<item>30 seconden</item>
<item>1 minuut</item>
<item>5 minuten</item>
<item>15 minuten</item>
<item>30 minuten</item>
<item>Nooit</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Klein</item> <item>Klein</item>
<item>Medium</item> <item>Medium</item>
@@ -526,7 +515,7 @@
<string name="advanced_unlock_tap_delete">Tik om geavanceerde ontgrendelingstoetsen te verwijderen</string> <string name="advanced_unlock_tap_delete">Tik om geavanceerde ontgrendelingstoetsen te verwijderen</string>
<string name="content">Inhoud</string> <string name="content">Inhoud</string>
<string name="device_credential">Apparaatreferentie</string> <string name="device_credential">Apparaatreferentie</string>
<string name="credential_before_click_advanced_unlock_button">Typ het wachtwoord en klik vervolgens op de knop \"Geavanceerd ontgrendelen\".</string> <string name="credential_before_click_advanced_unlock_button">Typ het wachtwoord en klik vervolgens op deze knop.</string>
<string name="advanced_unlock_prompt_not_initialized">Kan geavanceerde ontgrendelingsprompt niet initialiseren.</string> <string name="advanced_unlock_prompt_not_initialized">Kan geavanceerde ontgrendelingsprompt niet initialiseren.</string>
<string name="advanced_unlock_scanning_error">Geavanceerde ontgrendelingsfout: %1$s</string> <string name="advanced_unlock_scanning_error">Geavanceerde ontgrendelingsfout: %1$s</string>
<string name="advanced_unlock_not_recognized">Kan geavanceerde ontgrendelingsafdruk niet herkennen</string> <string name="advanced_unlock_not_recognized">Kan geavanceerde ontgrendelingsafdruk niet herkennen</string>
@@ -554,4 +543,13 @@
<string name="error_rebuild_list">Kan de lijst niet correct opnieuw opbouwen.</string> <string name="error_rebuild_list">Kan de lijst niet correct opnieuw opbouwen.</string>
<string name="error_database_uri_null">Database-URI kan niet worden opgehaald.</string> <string name="error_database_uri_null">Database-URI kan niet worden opgehaald.</string>
<string name="error_otp_type">Het bestaande OTP-type wordt niet herkend door dit formulier, de validatie ervan genereert het token mogelijk niet langer.</string> <string name="error_otp_type">Het bestaande OTP-type wordt niet herkend door dit formulier, de validatie ervan genereert het token mogelijk niet langer.</string>
<string name="icon_section_custom">Aangepast</string>
<string name="icon_section_standard">Standaard</string>
<string name="style_brightness_summary">Selecteer lichte of donkere thema\'s</string>
<string name="style_brightness_title">Helderheid van het thema</string>
<string name="error_remove_file">Er is een fout opgetreden bij het verwijderen van de bestandsgegevens.</string>
<string name="error_duplicate_file">De bestandsgegevens bestaan al.</string>
<string name="error_upload_file">Er is een fout opgetreden bij het uploaden van de bestandsgegevens.</string>
<string name="error_file_to_big">Het bestand dat je probeert te uploaden, is te groot.</string>
<string name="content_description_otp_information">Eenmalig wachtwoord-informatie</string>
</resources> </resources>

View File

@@ -120,17 +120,6 @@
<string name="uppercase">Store bokstavar</string> <string name="uppercase">Store bokstavar</string>
<string name="version_label">Utgåve %1$s</string> <string name="version_label">Utgåve %1$s</string>
<string name="education_unlock_summary">Skriv inn passordet og/eller nøkkelfil for å låsa opp databasen.</string> <string name="education_unlock_summary">Skriv inn passordet og/eller nøkkelfil for å låsa opp databasen.</string>
<string-array name="timeout_options">
<item>5 sekund</item>
<item>10 sekund</item>
<item>20 sekund</item>
<item>30 sekund</item>
<item>1 minutt</item>
<item>5 minutt</item>
<item>15 minutt</item>
<item>30 minutt</item>
<item>Aldri</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Liten</item> <item>Liten</item>
<item>Middels</item> <item>Middels</item>

View File

@@ -120,17 +120,6 @@
<string name="education_unlock_summary">prowadź hasło i/lub plik klucza, aby odblokować bazę danych. <string name="education_unlock_summary">prowadź hasło i/lub plik klucza, aby odblokować bazę danych.
\n \n
\nUtwórz kopię zapasową pliku bazy danych w bezpiecznym miejscu po każdej zmianie.</string> \nUtwórz kopię zapasową pliku bazy danych w bezpiecznym miejscu po każdej zmianie.</string>
<string-array name="timeout_options">
<item>5 sekund</item>
<item>10 sekund</item>
<item>20 sekund</item>
<item>30 sekund</item>
<item>1 minuta</item>
<item>5 minut</item>
<item>15 minut</item>
<item>30 minut</item>
<item>Nigdy</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Mała</item> <item>Mała</item>
<item>Średnia</item> <item>Średnia</item>
@@ -559,4 +548,6 @@
<string name="error_upload_file">Wystąpił błąd podczas przesyłania danych pliku.</string> <string name="error_upload_file">Wystąpił błąd podczas przesyłania danych pliku.</string>
<string name="error_file_to_big">Plik, który próbujesz przesłać, jest za duży.</string> <string name="error_file_to_big">Plik, który próbujesz przesłać, jest za duży.</string>
<string name="content_description_otp_information">Informacje o hasłach jednorazowych</string> <string name="content_description_otp_information">Informacje o hasłach jednorazowych</string>
<string name="error_remove_file">Wystąpił błąd podczas usuwania danych z pliku.</string>
<string name="error_duplicate_file">Dane pliku już istnieją.</string>
</resources> </resources>

View File

@@ -123,17 +123,6 @@
<string name="education_unlock_summary">Entre com a senha e/ou com o caminho para o arquivo-chave do banco de dados. <string name="education_unlock_summary">Entre com a senha e/ou com o caminho para o arquivo-chave do banco de dados.
\n \n
\nGuarde uma cópia do seu arquivo do banco em um lugar mais seguro depois de cada alteração.</string> \nGuarde uma cópia do seu arquivo do banco em um lugar mais seguro depois de cada alteração.</string>
<string-array name="timeout_options">
<item>5 segundos</item>
<item>10 segundos</item>
<item>20 segundos</item>
<item>30 segundos</item>
<item>1 minuto</item>
<item>5 minutos</item>
<item>15 minutos</item>
<item>30 minutos</item>
<item>Nunca</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Pequeno</item> <item>Pequeno</item>
<item>Médio</item> <item>Médio</item>

View File

@@ -139,17 +139,6 @@
<string name="education_unlock_summary">Entre com a palavra-passe e/ou com o caminho para o ficheiro-chave da base de dados. <string name="education_unlock_summary">Entre com a palavra-passe e/ou com o caminho para o ficheiro-chave da base de dados.
\n \n
\nGuarde uma cópia do seu ficheiro do banco num lugar mais seguro depois de cada alteração.</string> \nGuarde uma cópia do seu ficheiro do banco num lugar mais seguro depois de cada alteração.</string>
<string-array name="timeout_options">
<item>5 segundos</item>
<item>10 segundos</item>
<item>20 segundos</item>
<item>30 segundos</item>
<item>1 minuto</item>
<item>5 minutos</item>
<item>15 minutos</item>
<item>30 minutos</item>
<item>Nunca</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Pequena</item> <item>Pequena</item>
<item>Média</item> <item>Média</item>

View File

@@ -141,17 +141,6 @@
<string name="education_unlock_summary">Введите пароль и/или файл ключа, чтобы разблокировать базу. <string name="education_unlock_summary">Введите пароль и/или файл ключа, чтобы разблокировать базу.
\n \n
\nНе забывайте сохранять копию файла базы в безопасном месте после каждого изменения.</string> \nНе забывайте сохранять копию файла базы в безопасном месте после каждого изменения.</string>
<string-array name="timeout_options">
<item>5 секунд</item>
<item>10 секунд</item>
<item>20 секунд</item>
<item>30 секунд</item>
<item>1 минута</item>
<item>5 минут</item>
<item>15 минут</item>
<item>30 минут</item>
<item>Никогда</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Мелкий</item> <item>Мелкий</item>
<item>Обычный</item> <item>Обычный</item>
@@ -559,4 +548,6 @@
<string name="error_upload_file">Ошибка при загрузке данных файла.</string> <string name="error_upload_file">Ошибка при загрузке данных файла.</string>
<string name="error_file_to_big">Файл слишком большой для загрузки.</string> <string name="error_file_to_big">Файл слишком большой для загрузки.</string>
<string name="content_description_otp_information">Информация об одноразовом пароле</string> <string name="content_description_otp_information">Информация об одноразовом пароле</string>
<string name="error_duplicate_file">Данные файла уже существует.</string>
<string name="error_remove_file">Ошибка при удалении данных файла.</string>
</resources> </resources>

View File

@@ -120,17 +120,6 @@
<string name="uppercase">Veľké písmená</string> <string name="uppercase">Veľké písmená</string>
<string name="version_label">Version %1$s</string> <string name="version_label">Version %1$s</string>
<string name="education_unlock_summary">Vložte heslo a / alebo keyfile pre odomknutie databázy.</string> <string name="education_unlock_summary">Vložte heslo a / alebo keyfile pre odomknutie databázy.</string>
<string-array name="timeout_options">
<item>5 sekúnd</item>
<item>10 sekúnd</item>
<item>20 sekúnd</item>
<item>30 sekúnd</item>
<item>1 minúta</item>
<item>5 minút</item>
<item>15 minút</item>
<item>30 minút</item>
<item>Nikdy</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Malé</item> <item>Malé</item>
<item>Stredné</item> <item>Stredné</item>

View File

@@ -133,17 +133,6 @@
<string name="education_unlock_summary">Ange lösenord och/eller nyckelfil för att öppna databasen. <string name="education_unlock_summary">Ange lösenord och/eller nyckelfil för att öppna databasen.
\n \n
\nBacka upp databasfilen på ett säkert ställe efter varje ändring.</string> \nBacka upp databasfilen på ett säkert ställe efter varje ändring.</string>
<string-array name="timeout_options">
<item>5 sekunder</item>
<item>10 sekunder</item>
<item>20 sekunder</item>
<item>30 sekunder</item>
<item>1 minut</item>
<item>5 minuter</item>
<item>15 minuter</item>
<item>30 minuter</item>
<item>Aldrig</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Liten</item> <item>Liten</item>
<item>Medium</item> <item>Medium</item>

View File

@@ -543,4 +543,6 @@
<string name="error_upload_file">Dosya verileri karşıya yüklenirken bir hata oluştu.</string> <string name="error_upload_file">Dosya verileri karşıya yüklenirken bir hata oluştu.</string>
<string name="error_file_to_big">Karşıya yüklemeye çalıştığınız dosya çok büyük.</string> <string name="error_file_to_big">Karşıya yüklemeye çalıştığınız dosya çok büyük.</string>
<string name="content_description_otp_information">Tek seferlik parola bilgileri</string> <string name="content_description_otp_information">Tek seferlik parola bilgileri</string>
<string name="error_remove_file">Dosya verilerini kaldırırken bir hata oluştu.</string>
<string name="error_duplicate_file">Dosya verileri zaten var.</string>
</resources> </resources>

View File

@@ -123,17 +123,6 @@
<string name="education_unlock_summary">Введіть пароль та/або файл ключа, щоб відкрити базу даних. <string name="education_unlock_summary">Введіть пароль та/або файл ключа, щоб відкрити базу даних.
\n \n
\nСтворюйте резервну копію файлу бази даних після кожної внесеної зміни та зберігайте її у безпечному місці.</string> \nСтворюйте резервну копію файлу бази даних після кожної внесеної зміни та зберігайте її у безпечному місці.</string>
<string-array name="timeout_options">
<item>5 секунд</item>
<item>10 секунд</item>
<item>20 секунд</item>
<item>30 секунд</item>
<item>1 хвилина</item>
<item>5 хвилин</item>
<item>15 хвилин</item>
<item>30 хвилин</item>
<item>Ніколи</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Малий</item> <item>Малий</item>
<item>Середній</item> <item>Середній</item>
@@ -559,4 +548,6 @@
<string name="error_upload_file">Під час передавання даних файлу сталася помилка.</string> <string name="error_upload_file">Під час передавання даних файлу сталася помилка.</string>
<string name="error_file_to_big">Файл, який ви намагаєтеся передати, завеликий.</string> <string name="error_file_to_big">Файл, який ви намагаєтеся передати, завеликий.</string>
<string name="content_description_otp_information">Відомості про одноразовий пароль</string> <string name="content_description_otp_information">Відомості про одноразовий пароль</string>
<string name="error_remove_file">Сталася помилка під час вилучення даних файлу.</string>
<string name="error_duplicate_file">Дані файлу вже існують.</string>
</resources> </resources>

View File

@@ -119,17 +119,6 @@
<string name="education_unlock_summary">输入密码和/或密钥文件来解锁你的数据库。 <string name="education_unlock_summary">输入密码和/或密钥文件来解锁你的数据库。
\n \n
\n记得在每次做出更改后将数据库文件备份至安全的地方。</string> \n记得在每次做出更改后将数据库文件备份至安全的地方。</string>
<string-array name="timeout_options">
<item>5秒</item>
<item>10秒</item>
<item>20秒</item>
<item>30秒</item>
<item>1分钟</item>
<item>5分钟</item>
<item>15分钟</item>
<item>30分钟</item>
<item>从不</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item></item> <item></item>
<item></item> <item></item>
@@ -559,4 +548,6 @@
<string name="error_upload_file">上传文件数据时发生错误。</string> <string name="error_upload_file">上传文件数据时发生错误。</string>
<string name="error_file_to_big">你尝试上传的文件太大。</string> <string name="error_file_to_big">你尝试上传的文件太大。</string>
<string name="content_description_otp_information">一次性密码信息</string> <string name="content_description_otp_information">一次性密码信息</string>
<string name="error_remove_file">删除文件数据时发生了一个错误。</string>
<string name="error_duplicate_file">文件数据已存在。</string>
</resources> </resources>

View File

@@ -117,17 +117,6 @@
<string name="unsupported_db_version">不支援的資料庫版本。</string> <string name="unsupported_db_version">不支援的資料庫版本。</string>
<string name="uppercase">大寫</string> <string name="uppercase">大寫</string>
<string name="education_unlock_summary">輸入密碼和/或一個密鑰檔來解鎖你的資料庫.</string> <string name="education_unlock_summary">輸入密碼和/或一個密鑰檔來解鎖你的資料庫.</string>
<string-array name="timeout_options">
<item>5秒</item>
<item>10秒</item>
<item>20秒</item>
<item>30秒</item>
<item>1分鐘</item>
<item>5分鐘</item>
<item>15分鐘</item>
<item>30分鐘</item>
<item>從不</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item></item> <item></item>
<item></item> <item></item>

View File

@@ -47,6 +47,7 @@
<string name="database_file_name_default" translatable="false">keepass</string> <string name="database_file_name_default" translatable="false">keepass</string>
<string name="database_file_extension_default" translatable="false">.kdbx</string> <string name="database_file_extension_default" translatable="false">.kdbx</string>
<string name="database_default_name" translatable="false">KeePassDX Database</string> <string name="database_default_name" translatable="false">KeePassDX Database</string>
<string name="app_properties_file_name" translatable="false">keepassdx.properties</string>
<!-- <!--
******************* *******************
@@ -105,6 +106,9 @@
<string name="temp_advanced_unlock_timeout_default" translatable="false">36000000</string> <string name="temp_advanced_unlock_timeout_default" translatable="false">36000000</string>
<string name="biometric_delete_all_key_key" translatable="false">biometric_delete_all_key_key</string> <string name="biometric_delete_all_key_key" translatable="false">biometric_delete_all_key_key</string>
<string name="import_app_properties_key" translatable="false">import_app_properties_key</string>
<string name="export_app_properties_key" translatable="false">export_app_properties_key</string>
<!-- Form Filling Settings --> <!-- Form Filling Settings -->
<string name="settings_form_filling_key" translatable="false">settings_form_filling_key</string> <string name="settings_form_filling_key" translatable="false">settings_form_filling_key</string>
@@ -284,31 +288,6 @@
<string name="timeout_backup_key" translatable="false">timeout_backup_key</string> <string name="timeout_backup_key" translatable="false">timeout_backup_key</string>
<string name="timeout_default" translatable="false">300000</string> <string name="timeout_default" translatable="false">300000</string>
<string name="timeout_screen_off" translatable="false">1500</string> <string name="timeout_screen_off" translatable="false">1500</string>
<string-array name="timeout_values">
<item translatable="false">5000</item>
<item translatable="false">10000</item>
<item translatable="false">20000</item>
<item translatable="false">30000</item>
<item translatable="false">60000</item>
<item translatable="false">300000</item>
<item translatable="false">900000</item>
<item translatable="false">1800000</item>
<item translatable="false">-1</item>
</string-array>
<string-array name="large_timeout_values">
<item translatable="false">300000</item>
<item translatable="false">900000</item>
<item translatable="false">1800000</item>
<item translatable="false">3600000</item>
<item translatable="false">7200000</item>
<item translatable="false">18000000</item>
<item translatable="false">36000000</item>
<item translatable="false">86400000</item>
<item translatable="false">172800000</item>
<item translatable="false">604800000</item>
<item translatable="false">2592000000</item>
<item translatable="false">-1</item>
</string-array>
<!-- Text Size --> <!-- Text Size -->
<dimen name="list_icon_size_default" translatable="false">32dp</dimen> <dimen name="list_icon_size_default" translatable="false">32dp</dimen>

View File

@@ -31,7 +31,7 @@
<string name="encryption">Encryption</string> <string name="encryption">Encryption</string>
<string name="encryption_algorithm">Encryption algorithm</string> <string name="encryption_algorithm">Encryption algorithm</string>
<string name="key_derivation_function">Key derivation function</string> <string name="key_derivation_function">Key derivation function</string>
<string name="app_timeout">App timeout</string> <string name="app_timeout">Timeout</string>
<string name="app_timeout_summary">Idle time before locking the database</string> <string name="app_timeout_summary">Idle time before locking the database</string>
<string name="application">App</string> <string name="application">App</string>
<string name="brackets">Brackets</string> <string name="brackets">Brackets</string>
@@ -142,6 +142,7 @@
<string name="error_upload_file">An error occurred while uploading the file data.</string> <string name="error_upload_file">An error occurred while uploading the file data.</string>
<string name="error_duplicate_file">The file data already exists.</string> <string name="error_duplicate_file">The file data already exists.</string>
<string name="error_remove_file">An error occurred while removing the file data.</string> <string name="error_remove_file">An error occurred while removing the file data.</string>
<string name="error_start_database_action">An error occurred while performing an action on the database.</string>
<string name="field_name">Field name</string> <string name="field_name">Field name</string>
<string name="field_value">Field value</string> <string name="field_value">Field value</string>
<string name="file_not_found_content">Could not find file. Try reopening it from your file browser.</string> <string name="file_not_found_content">Could not find file. Try reopening it from your file browser.</string>
@@ -233,6 +234,15 @@
<string name="show_recent_files_summary">Show locations of recent databases</string> <string name="show_recent_files_summary">Show locations of recent databases</string>
<string name="hide_broken_locations_title">Hide broken database links</string> <string name="hide_broken_locations_title">Hide broken database links</string>
<string name="hide_broken_locations_summary">Hide broken links in the list of recent databases</string> <string name="hide_broken_locations_summary">Hide broken links in the list of recent databases</string>
<string name="import_app_properties_title">Import app properties</string>
<string name="import_app_properties_summary">Select a file to import app properties</string>
<string name="export_app_properties_title">Export app properties</string>
<string name="export_app_properties_summary">Create a file to export app properties</string>
<string name="description_app_properties">KeePassDX properties to manage app settings</string>
<string name="success_import_app_properties">App properties imported</string>
<string name="error_import_app_properties">Error during app properties importation</string>
<string name="success_export_app_properties">App properties exported</string>
<string name="error_export_app_properties">Error during app properties exportation</string>
<string name="root">Root</string> <string name="root">Root</string>
<string name="encryption_explanation">Database encryption algorithm used for all data.</string> <string name="encryption_explanation">Database encryption algorithm used for all data.</string>
<string name="kdf_explanation">To generate the key for the encryption algorithm, the master key is transformed using a randomly salted key derivation function.</string> <string name="kdf_explanation">To generate the key for the encryption algorithm, the master key is transformed using a randomly salted key derivation function.</string>
@@ -301,6 +311,7 @@
<string name="advanced_unlock_prompt_not_initialized">Unable to initialize advanced unlock prompt.</string> <string name="advanced_unlock_prompt_not_initialized">Unable to initialize advanced unlock prompt.</string>
<string name="credential_before_click_advanced_unlock_button">Type in the password, and then click this button.</string> <string name="credential_before_click_advanced_unlock_button">Type in the password, and then click this button.</string>
<string name="database_history">History</string> <string name="database_history">History</string>
<string name="properties">Properties</string>
<string name="menu_appearance_settings">Appearance</string> <string name="menu_appearance_settings">Appearance</string>
<string name="biometric">Biometric</string> <string name="biometric">Biometric</string>
<string name="device_credential">Device credential</string> <string name="device_credential">Device credential</string>
@@ -524,31 +535,6 @@
<string name="unit_kibibyte">KiB</string> <string name="unit_kibibyte">KiB</string>
<string name="unit_mebibyte">MiB</string> <string name="unit_mebibyte">MiB</string>
<string name="unit_gibibyte">GiB</string> <string name="unit_gibibyte">GiB</string>
<string-array name="timeout_options">
<item>5 seconds</item>
<item>10 seconds</item>
<item>20 seconds</item>
<item>30 seconds</item>
<item>1 minute</item>
<item>5 minutes</item>
<item>15 minutes</item>
<item>30 minutes</item>
<item>Never</item>
</string-array>
<string-array name="large_timeout_options">
<item>5 minutes</item>
<item>15 minutes</item>
<item>30 minutes</item>
<item>1 hour</item>
<item>2 hours</item>
<item>5 hours</item>
<item>10 hours</item>
<item>24 hours</item>
<item>48 hours</item>
<item>1 week</item>
<item>1 month</item>
<item>Never</item>
</string-array>
<string-array name="list_size_options"> <string-array name="list_size_options">
<item>Small</item> <item>Small</item>
<item>Medium</item> <item>Medium</item>

View File

@@ -47,13 +47,11 @@
android:title="@string/temp_advanced_unlock_enable_title" android:title="@string/temp_advanced_unlock_enable_title"
android:summary="@string/temp_advanced_unlock_enable_summary" android:summary="@string/temp_advanced_unlock_enable_summary"
android:defaultValue="@bool/temp_advanced_unlock_enable_default"/> android:defaultValue="@bool/temp_advanced_unlock_enable_default"/>
<ListPreference <com.kunzisoft.keepass.settings.preference.DurationDialogPreference
android:key="@string/temp_advanced_unlock_timeout_key" android:key="@string/temp_advanced_unlock_timeout_key"
android:title="@string/temp_advanced_unlock_timeout_title" android:title="@string/temp_advanced_unlock_timeout_title"
android:summary="@string/temp_advanced_unlock_timeout_summary" android:summary="@string/temp_advanced_unlock_timeout_summary"
android:dependency="@string/temp_advanced_unlock_enable_key" android:dependency="@string/temp_advanced_unlock_enable_key"
android:entries="@array/large_timeout_options"
android:entryValues="@array/large_timeout_values"
android:dialogTitle="@string/advanced_unlock_timeout" android:dialogTitle="@string/advanced_unlock_timeout"
android:defaultValue="@string/temp_advanced_unlock_timeout_default"/> android:defaultValue="@string/temp_advanced_unlock_timeout_default"/>
<Preference <Preference

View File

@@ -69,12 +69,10 @@
<PreferenceCategory <PreferenceCategory
android:title="@string/lock"> android:title="@string/lock">
<ListPreference <com.kunzisoft.keepass.settings.preference.DurationDialogPreference
android:key="@string/app_timeout_key" android:key="@string/app_timeout_key"
android:title="@string/app_timeout" android:title="@string/app_timeout"
android:summary="@string/app_timeout_summary" android:summary="@string/app_timeout_summary"
android:entries="@array/timeout_options"
android:entryValues="@array/timeout_values"
android:dialogTitle="@string/app_timeout" android:dialogTitle="@string/app_timeout"
android:defaultValue="@string/timeout_default"/> android:defaultValue="@string/timeout_default"/>
<SwitchPreference <SwitchPreference
@@ -150,4 +148,18 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory
android:title="@string/properties">
<Preference
android:key="@string/import_app_properties_key"
android:title="@string/import_app_properties_title"
android:summary="@string/import_app_properties_summary"/>
<Preference
android:key="@string/export_app_properties_key"
android:title="@string/export_app_properties_title"
android:summary="@string/export_app_properties_summary"/>
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -66,12 +66,10 @@
android:summary="@string/clear_clipboard_notification_summary" android:summary="@string/clear_clipboard_notification_summary"
android:dependency="@string/clipboard_notifications_key" android:dependency="@string/clipboard_notifications_key"
android:defaultValue="@bool/clear_clipboard_notification_default"/> android:defaultValue="@bool/clear_clipboard_notification_default"/>
<ListPreference <com.kunzisoft.keepass.settings.preference.DurationDialogPreference
android:key="@string/clipboard_timeout_key" android:key="@string/clipboard_timeout_key"
android:title="@string/clipboard_timeout" android:title="@string/clipboard_timeout"
android:summary="@string/clipboard_timeout_summary" android:summary="@string/clipboard_timeout_summary"
android:entries="@array/timeout_options"
android:entryValues="@array/timeout_values"
android:dialogTitle="@string/clipboard_timeout" android:dialogTitle="@string/clipboard_timeout"
android:defaultValue="@string/clipboard_timeout_default"/> android:defaultValue="@string/clipboard_timeout_default"/>
<SwitchPreference <SwitchPreference

View File

@@ -31,12 +31,10 @@
android:summary="@string/keyboard_notification_entry_clear_close_summary" android:summary="@string/keyboard_notification_entry_clear_close_summary"
android:dependency="@string/keyboard_notification_entry_key" android:dependency="@string/keyboard_notification_entry_key"
android:defaultValue="@bool/keyboard_notification_entry_clear_close_default"/> android:defaultValue="@bool/keyboard_notification_entry_clear_close_default"/>
<ListPreference <com.kunzisoft.keepass.settings.preference.DurationDialogPreference
android:key="@string/keyboard_entry_timeout_key" android:key="@string/keyboard_entry_timeout_key"
android:title="@string/keyboard_entry_timeout_title" android:title="@string/keyboard_entry_timeout_title"
android:summary="@string/keyboard_entry_timeout_summary" android:summary="@string/keyboard_entry_timeout_summary"
android:entries="@array/timeout_options"
android:entryValues="@array/timeout_values"
android:dialogTitle="@string/keyboard_entry_timeout_title" android:dialogTitle="@string/keyboard_entry_timeout_title"
android:defaultValue="@string/keyboard_entry_timeout_default" android:defaultValue="@string/keyboard_entry_timeout_default"
android:dependency="@string/keyboard_notification_entry_clear_close_key"/> android:dependency="@string/keyboard_notification_entry_clear_close_key"/>

View File

@@ -38,8 +38,12 @@ object CipherFactory {
} }
@Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class) @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
fun getTwofish(opmode: Int, key: ByteArray, IV: ByteArray): Cipher { fun getTwofish(opmode: Int, key: ByteArray, IV: ByteArray, forceCompatibility: Boolean = false): Cipher {
val cipher: Cipher = Cipher.getInstance("Twofish/CBC/PKCS7PADDING") val cipher: Cipher = if (forceCompatibility) {
Cipher.getInstance("Twofish/CBC/NoPadding")
} else {
Cipher.getInstance("Twofish/CBC/PKCS7PADDING")
}
cipher.init(opmode, SecretKeySpec(key, "AES"), IvParameterSpec(IV)) cipher.init(opmode, SecretKeySpec(key, "AES"), IvParameterSpec(IV))
return cipher return cipher
} }

View File

@@ -1,2 +1,4 @@
* Fix themes #935 * Fix themes #935 #926
* Decrease default clipboard time #934 * Decrease default clipboard time #934
* Better opening performance #929 #933
* Fix memory usage setting #941

View File

@@ -0,0 +1 @@
* Fix small bugs #948

View File

@@ -0,0 +1,3 @@
* Import / Export app properties #839
* Force twofish padding compatibility #955
* Better timeout preference #579

View File

@@ -1,2 +1,4 @@
* Correction des themes #935 * Correction des themes #935 #926
* Baisse du temps du presse-papier par defaut #934 * Baisse du temps du presse-papier par defaut #934
* Meilleures performaces d'ouverture #929 #933
* Correction du paramètre d'usage mémoire #941

View File

@@ -0,0 +1 @@
* Correction de petits bugs #948

View File

@@ -0,0 +1,3 @@
* Import / Export des propriétés de l'application #839
* Force la compatibilité du bourrage twofish #955
* Meilleure préférence d'expiration #579