diff --git a/CHANGELOG b/CHANGELOG index 6acc3f201..23aeb1c88 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +KeePassDX(4.1.8) + * Updated to API 35 minimum SDK 19 #2073 #2138 #2067 #2133 #1687 (Thx @Dev-ClayP) + * Remember last read-only state #2099 #2100 (Thx @rmacklin) + * Fix merge deletion #1516 + * Fix space in search #175 + * Fix deletable recycle bin #2163 + * Small fixes + KeePassDX(4.1.7) * Fix CipherDatabase for biometric states #2119 diff --git a/app/build.gradle b/app/build.gradle index af6e17e18..090ae8234 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,14 +5,14 @@ apply plugin: 'kotlin-kapt' android { namespace 'com.kunzisoft.keepass' - compileSdkVersion 34 + compileSdkVersion 36 defaultConfig { applicationId "com.kunzisoft.keepass" - minSdkVersion 15 - targetSdkVersion 34 - versionCode = 139 - versionName = "4.1.7" + minSdkVersion 19 + targetSdkVersion 35 + versionCode = 141 + versionName = "4.1.8" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" diff --git a/app/schemas/com.kunzisoft.keepass.app.database.AppDatabase/3.json b/app/schemas/com.kunzisoft.keepass.app.database.AppDatabase/3.json new file mode 100644 index 000000000..f9b3b72ef --- /dev/null +++ b/app/schemas/com.kunzisoft.keepass.app.database.AppDatabase/3.json @@ -0,0 +1,96 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "a20aec7cf09664b1102ec659fa51160a", + "entities": [ + { + "tableName": "file_database_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `database_alias` TEXT NOT NULL, `keyfile_uri` TEXT, `hardware_key` TEXT, `read_only` INTEGER, `updated` INTEGER NOT NULL, PRIMARY KEY(`database_uri`))", + "fields": [ + { + "fieldPath": "databaseUri", + "columnName": "database_uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseAlias", + "columnName": "database_alias", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "keyFileUri", + "columnName": "keyfile_uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hardwareKey", + "columnName": "hardware_key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "readOnly", + "columnName": "read_only", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "updated", + "columnName": "updated", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "database_uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "cipher_database", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`database_uri` TEXT NOT NULL, `encrypted_value` TEXT NOT NULL, `specs_parameters` TEXT NOT NULL, PRIMARY KEY(`database_uri`))", + "fields": [ + { + "fieldPath": "databaseUri", + "columnName": "database_uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedValue", + "columnName": "encrypted_value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "specParameters", + "columnName": "specs_parameters", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "database_uri" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a20aec7cf09664b1102ec659fa51160a')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/igreenwood/loupe/Loupe.kt b/app/src/main/java/com/igreenwood/loupe/Loupe.kt index a6f36bb45..de9f45242 100644 --- a/app/src/main/java/com/igreenwood/loupe/Loupe.kt +++ b/app/src/main/java/com/igreenwood/loupe/Loupe.kt @@ -358,78 +358,41 @@ class Loupe(imageView: ImageView, container: ViewGroup) : View.OnTouchListener, val imageView = imageViewRef.get() ?: return isViewTranslateAnimationRunning = true - - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - imageView.run { - val translationY = if (velY > 0) { - originalViewBounds.top + height - top - } else { - originalViewBounds.top - height - top - } - animate() - .setDuration(dismissAnimationDuration) - .setInterpolator(dismissAnimationInterpolator) - .translationY(translationY.toFloat()) - .setUpdateListener { - val amount = calcTranslationAmount() - changeBackgroundAlpha(amount) - onViewTranslateListener?.onViewTranslate(imageView, amount) - } - .setListener(object : Animator.AnimatorListener { - override fun onAnimationStart(p0: Animator) { - - } - - override fun onAnimationEnd(p0: Animator) { - isViewTranslateAnimationRunning = false - onViewTranslateListener?.onDismiss(imageView) - cleanup() - } - - override fun onAnimationCancel(p0: Animator) { - isViewTranslateAnimationRunning = false - } - - override fun onAnimationRepeat(p0: Animator) { - // no op - } - }) - } - } else { - ObjectAnimator.ofFloat(imageView, View.TRANSLATION_Y, if (velY > 0) { - originalViewBounds.top + imageView.height - imageView.top + + imageView.run { + val translationY = if (velY > 0) { + originalViewBounds.top + height - top } else { - originalViewBounds.top - imageView.height - imageView.top - }.toFloat()).apply { - duration = dismissAnimationDuration - interpolator = dismissAnimationInterpolator - addUpdateListener { - val amount = calcTranslationAmount() - changeBackgroundAlpha(amount) - onViewTranslateListener?.onViewTranslate(imageView, amount) - } - addListener(object : Animator.AnimatorListener { - override fun onAnimationStart(p0: Animator) { - // no op - } - - override fun onAnimationEnd(p0: Animator) { - isViewTranslateAnimationRunning = false - onViewTranslateListener?.onDismiss(imageView) - cleanup() - } - - override fun onAnimationCancel(p0: Animator) { - isViewTranslateAnimationRunning = false - } - - override fun onAnimationRepeat(p0: Animator) { - // no op - } - }) - start() + originalViewBounds.top - height - top } + animate() + .setDuration(dismissAnimationDuration) + .setInterpolator(dismissAnimationInterpolator) + .translationY(translationY.toFloat()) + .setUpdateListener { + val amount = calcTranslationAmount() + changeBackgroundAlpha(amount) + onViewTranslateListener?.onViewTranslate(imageView, amount) + } + .setListener(object : Animator.AnimatorListener { + override fun onAnimationStart(p0: Animator) { + + } + + override fun onAnimationEnd(p0: Animator) { + isViewTranslateAnimationRunning = false + onViewTranslateListener?.onDismiss(imageView) + cleanup() + } + + override fun onAnimationCancel(p0: Animator) { + isViewTranslateAnimationRunning = false + } + + override fun onAnimationRepeat(p0: Animator) { + // no op + } + }) } } @@ -657,137 +620,76 @@ class Loupe(imageView: ImageView, container: ViewGroup) : View.OnTouchListener, private fun restoreViewTransform() { val imageView = imageViewRef.get() ?: return - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - imageView.run { - animate() - .setDuration(restoreAnimationDuration) - .setInterpolator(restoreAnimationInterpolator) - .translationY((originalViewBounds.top - top).toFloat()) - .setUpdateListener { - val amount = calcTranslationAmount() - changeBackgroundAlpha(amount) - onViewTranslateListener?.onViewTranslate(this, amount) + imageView.run { + animate() + .setDuration(restoreAnimationDuration) + .setInterpolator(restoreAnimationInterpolator) + .translationY((originalViewBounds.top - top).toFloat()) + .setUpdateListener { + val amount = calcTranslationAmount() + changeBackgroundAlpha(amount) + onViewTranslateListener?.onViewTranslate(this, amount) + } + .setListener(object : Animator.AnimatorListener { + override fun onAnimationStart(p0: Animator) { + // no op } - .setListener(object : Animator.AnimatorListener { - override fun onAnimationStart(p0: Animator) { - // no op - } - override fun onAnimationEnd(p0: Animator) { - onViewTranslateListener?.onRestore(imageView) - } + override fun onAnimationEnd(p0: Animator) { + onViewTranslateListener?.onRestore(imageView) + } - override fun onAnimationCancel(p0: Animator) { - // no op - } + override fun onAnimationCancel(p0: Animator) { + // no op + } - override fun onAnimationRepeat(p0: Animator) { - // no op - } - }) - } - } else { - ObjectAnimator.ofFloat(imageView, View.TRANSLATION_Y, (originalViewBounds.top - imageView.top).toFloat()).apply { - duration = restoreAnimationDuration - interpolator = restoreAnimationInterpolator - addUpdateListener { - val amount = calcTranslationAmount() - changeBackgroundAlpha(amount) - onViewTranslateListener?.onViewTranslate(imageView, amount) - } - addListener(object : Animator.AnimatorListener { - override fun onAnimationStart(p0: Animator) { - // no op - } - - override fun onAnimationEnd(p0: Animator) { - onViewTranslateListener?.onRestore(imageView) - } - - override fun onAnimationCancel(p0: Animator) { - // no op - } - - override fun onAnimationRepeat(p0: Animator) { - // no op - } - }) - start() - } + override fun onAnimationRepeat(p0: Animator) { + // no op + } + }) } + } private fun startDragToDismissAnimation() { val imageView = imageViewRef.get() ?: return - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - imageView.run { - val translationY = if (y - initialY > 0) { - originalViewBounds.top + height - top - } else { - originalViewBounds.top - height - top - } - animate() - .setDuration(dismissAnimationDuration) - .setInterpolator(AccelerateDecelerateInterpolator()) - .translationY(translationY.toFloat()) - .setUpdateListener { - val amount = calcTranslationAmount() - changeBackgroundAlpha(amount) - onViewTranslateListener?.onViewTranslate(this, amount) + + imageView.run { + val translationY = if (y - initialY > 0) { + originalViewBounds.top + height - top + } else { + originalViewBounds.top - height - top + } + animate() + .setDuration(dismissAnimationDuration) + .setInterpolator(AccelerateDecelerateInterpolator()) + .translationY(translationY.toFloat()) + .setUpdateListener { + val amount = calcTranslationAmount() + changeBackgroundAlpha(amount) + onViewTranslateListener?.onViewTranslate(this, amount) + } + .setListener(object : Animator.AnimatorListener { + override fun onAnimationStart(p0: Animator) { + isViewTranslateAnimationRunning = true } - .setListener(object : Animator.AnimatorListener { - override fun onAnimationStart(p0: Animator) { - isViewTranslateAnimationRunning = true - } - override fun onAnimationEnd(p0: Animator) { - isViewTranslateAnimationRunning = false - onViewTranslateListener?.onDismiss(imageView) - cleanup() - } + override fun onAnimationEnd(p0: Animator) { + isViewTranslateAnimationRunning = false + onViewTranslateListener?.onDismiss(imageView) + cleanup() + } - override fun onAnimationCancel(p0: Animator) { - isViewTranslateAnimationRunning = false - } + override fun onAnimationCancel(p0: Animator) { + isViewTranslateAnimationRunning = false + } - override fun onAnimationRepeat(p0: Animator) { - // no op - } - }) - } - } else { - ObjectAnimator.ofFloat(imageView, View.TRANSLATION_Y, imageView.translationY).apply { - duration = dismissAnimationDuration - interpolator = AccelerateDecelerateInterpolator() - addUpdateListener { - val amount = calcTranslationAmount() - changeBackgroundAlpha(amount) - onViewTranslateListener?.onViewTranslate(imageView, amount) - } - addListener(object : Animator.AnimatorListener { - override fun onAnimationStart(p0: Animator) { - isViewTranslateAnimationRunning = true - } - - override fun onAnimationEnd(p0: Animator) { - isViewTranslateAnimationRunning = false - onViewTranslateListener?.onDismiss(imageView) - cleanup() - } - - override fun onAnimationCancel(p0: Animator) { - isViewTranslateAnimationRunning = false - } - - override fun onAnimationRepeat(p0: Animator) { - // no op - } - }) - start() - } + override fun onAnimationRepeat(p0: Animator) { + // no op + } + }) } + } private fun processFlingToDismiss(velocityY: Float) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt index e86e8f690..d117418f4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt @@ -58,7 +58,7 @@ class AboutActivity : StylishActivity() { var version: String var build: String try { - version = packageManager.getPackageInfoCompat(packageName).versionName + version = packageManager.getPackageInfoCompat(packageName).versionName ?: "" build = BuildConfig.BUILD_VERSION } catch (e: NameNotFoundException) { Log.w(javaClass.simpleName, "Unable to get the app or the build version", e) @@ -78,9 +78,8 @@ class AboutActivity : StylishActivity() { movementMethod = LinkMovementMethod.getInstance() text = HtmlCompat.fromHtml(getString(R.string.html_about_licence, DateTime().year), HtmlCompat.FROM_HTML_MODE_LEGACY) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - textDirection = View.TEXT_DIRECTION_ANY_RTL - } + textDirection = View.TEXT_DIRECTION_ANY_RTL + } findViewById(R.id.activity_about_privacy_text).apply { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index ce9a54fd0..49f6544c6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -604,16 +604,12 @@ class EntryEditActivity : DatabaseLockActivity(), isVisible = isEnabled } menu?.findItem(R.id.menu_add_attachment)?.apply { - // Attachment not compatible below KitKat isEnabled = !mIsTemplate - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT isVisible = isEnabled } menu?.findItem(R.id.menu_add_otp)?.apply { - // OTP not compatible below KitKat isEnabled = mAllowOTP && !mIsTemplate - && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT isVisible = isEnabled } return super.onPrepareOptionsMenu(menu) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 2ba2638d3..dffc3c586 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -70,7 +70,6 @@ import com.kunzisoft.keepass.utils.MagikeyboardUtil import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.utils.UriUtil.isContributingUser import com.kunzisoft.keepass.utils.UriUtil.openUrl -import com.kunzisoft.keepass.utils.allowCreateDocumentByStorageAccessFramework import com.kunzisoft.keepass.utils.getParcelableCompat import com.kunzisoft.keepass.view.asError import com.kunzisoft.keepass.view.showActionErrorIfNeeded @@ -263,7 +262,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(), GroupActivity.launch( this@FileDatabaseSelectActivity, database, - PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity) + false ) } ACTION_DATABASE_LOAD_TASK -> { @@ -330,13 +329,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(), // Show open and create button or special mode when (mSpecialMode) { SpecialMode.DEFAULT -> { - if (packageManager.allowCreateDocumentByStorageAccessFramework()) { - // There is an activity which can handle this intent. - createDatabaseButtonView?.visibility = View.VISIBLE - } else{ - // No Activity found that can handle this intent. - createDatabaseButtonView?.visibility = View.GONE - } + createDatabaseButtonView?.visibility = View.VISIBLE } else -> { // Disable create button if in selection mode or request for autofill diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt index 03aacc6f2..c0704dc0a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -1373,7 +1373,8 @@ class GroupActivity : DatabaseLockActivity(), } else -> { // Load the previous group - loadMainGroup(mPreviousGroupsIds.removeLast()) + loadMainGroup(mPreviousGroupsIds + .removeAt(mPreviousGroupsIds.lastIndex)) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt index de0991f5e..4bca99154 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt @@ -43,6 +43,7 @@ import androidx.biometric.BiometricManager import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.commit import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.snackbar.Snackbar @@ -52,6 +53,7 @@ import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.SpecialMode import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity +import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.autofill.AutofillComponent import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.biometric.DeviceUnlockFragment @@ -66,6 +68,7 @@ import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.model.CipherDecryptDatabase import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.model.CredentialStorage +import com.kunzisoft.keepass.model.DatabaseFile import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK @@ -73,8 +76,8 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion. import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.MAIN_CREDENTIAL_KEY import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.READ_ONLY_KEY -import com.kunzisoft.keepass.settings.DeviceUnlockSettingsActivity import com.kunzisoft.keepass.settings.AppearanceSettingsActivity +import com.kunzisoft.keepass.settings.DeviceUnlockSettingsActivity import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION @@ -105,7 +108,11 @@ class MainCredentialActivity : DatabaseModeActivity() { private var deviceUnlockFragment: DeviceUnlockFragment? = null private val mDatabaseFileViewModel: DatabaseFileViewModel by viewModels() - private val mDeviceUnlockViewModel: DeviceUnlockViewModel by viewModels() + private val mDeviceUnlockViewModel: DeviceUnlockViewModel? by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ViewModelProvider(this)[DeviceUnlockViewModel::class.java] + } else null + } private val mPasswordActivityEducation = PasswordActivityEducation(this) @@ -147,7 +154,7 @@ class MainCredentialActivity : DatabaseModeActivity() { mReadOnly = if (savedInstanceState != null && savedInstanceState.containsKey(KEY_READ_ONLY)) { savedInstanceState.getBoolean(KEY_READ_ONLY) } else { - PreferencesUtil.enableReadOnlyDatabase(this) + false } mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this) mRememberHardwareKey = PreferencesUtil.rememberHardwareKey(this) @@ -175,7 +182,7 @@ class MainCredentialActivity : DatabaseModeActivity() { // Listen password checkbox to init advanced unlock and confirmation button mainCredentialView?.onConditionToStoreCredentialChanged = { _, verified -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.checkConditionToStoreCredential( + mDeviceUnlockViewModel?.checkConditionToStoreCredential( condition = verified ) } @@ -203,6 +210,13 @@ class MainCredentialActivity : DatabaseModeActivity() { } mForceReadOnly = databaseFileNotExists + // Restore read-only state from database file if not forced + if (!mForceReadOnly) { + databaseFile?.readOnly?.let { savedReadOnlyState -> + mReadOnly = savedReadOnlyState + } + } + invalidateOptionsMenu() // Post init uri with KeyFile only if needed @@ -233,29 +247,31 @@ class MainCredentialActivity : DatabaseModeActivity() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.uiState.collect { uiState -> - // New value received - uiState.credentialRequiredCipher?.let { cipher -> - mDeviceUnlockViewModel.encryptCredential( - credential = getCredentialForEncryption(), - cipher = cipher - ) - } - uiState.cipherEncryptDatabase?.let { cipherEncryptDatabase -> - onCredentialEncrypted(cipherEncryptDatabase) - mDeviceUnlockViewModel.consumeCredentialEncrypted() - } - uiState.cipherDecryptDatabase?.let { cipherDecryptDatabase -> - onCredentialDecrypted(cipherDecryptDatabase) - mDeviceUnlockViewModel.consumeCredentialDecrypted() - } - uiState.exception?.let { error -> - Snackbar.make( - coordinatorLayout, - deviceUnlockError(error, this@MainCredentialActivity), - Snackbar.LENGTH_LONG - ).asError().show() - mDeviceUnlockViewModel.exceptionShown() + mDeviceUnlockViewModel?.let { deviceUnlockViewModel -> + deviceUnlockViewModel.uiState.collect { uiState -> + // New value received + uiState.credentialRequiredCipher?.let { cipher -> + deviceUnlockViewModel.encryptCredential( + credential = getCredentialForEncryption(), + cipher = cipher + ) + } + uiState.cipherEncryptDatabase?.let { cipherEncryptDatabase -> + onCredentialEncrypted(cipherEncryptDatabase) + deviceUnlockViewModel.consumeCredentialEncrypted() + } + uiState.cipherDecryptDatabase?.let { cipherDecryptDatabase -> + onCredentialDecrypted(cipherDecryptDatabase) + deviceUnlockViewModel.consumeCredentialDecrypted() + } + uiState.exception?.let { error -> + Snackbar.make( + coordinatorLayout, + deviceUnlockError(error, this@MainCredentialActivity), + Snackbar.LENGTH_LONG + ).asError().show() + deviceUnlockViewModel.exceptionShown() + } } } } @@ -508,7 +524,7 @@ class MainCredentialActivity : DatabaseModeActivity() { } else { // Init Biometric elements if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.connect(databaseFileUri) + mDeviceUnlockViewModel?.connect(databaseFileUri) } } @@ -652,7 +668,7 @@ class MainCredentialActivity : DatabaseModeActivity() { try { menu.findItem(R.id.menu_open_file_read_mode_key) } catch (e: Exception) { - Log.e(TAG, "Unable to find read mode menu") + Log.e(TAG, "Unable to find read mode menu", e) } performedNextEducation(menu) }, @@ -681,7 +697,7 @@ class MainCredentialActivity : DatabaseModeActivity() { }) } } - } catch (ignored: Exception) {} + } catch (_: Exception) {} } } @@ -702,6 +718,12 @@ class MainCredentialActivity : DatabaseModeActivity() { R.id.menu_open_file_read_mode_key -> { mReadOnly = !mReadOnly changeOpenFileReadIcon(item) + // Save the read-only state to database + mDatabaseFileUri?.let { databaseUri -> + FileDatabaseHistoryAction.getInstance(applicationContext).addOrUpdateDatabaseFile( + DatabaseFile(databaseUri = databaseUri, readOnly = mReadOnly) + ) + } } else -> MenuUtil.onDefaultMenuOptionsItemSelected(this, item) } @@ -712,7 +734,7 @@ class MainCredentialActivity : DatabaseModeActivity() { override fun onDestroy() { super.onDestroy() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.disconnect() + mDeviceUnlockViewModel?.disconnect() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt index 45cbb9db1..78972aa76 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt @@ -176,21 +176,14 @@ class SortDialogFragment : DatabaseDialogFragment() { return bundle } - fun getInstance(sortNodeEnum: SortNodeEnum, - ascending: Boolean, - groupsBefore: Boolean): SortDialogFragment { - val bundle = buildBundle(sortNodeEnum, ascending, groupsBefore) - val fragment = SortDialogFragment() - fragment.arguments = bundle - return fragment - } - fun getInstance(sortNodeEnum: SortNodeEnum, ascending: Boolean, groupsBefore: Boolean, - recycleBinBottom: Boolean): SortDialogFragment { + recycleBinBottom: Boolean?): SortDialogFragment { val bundle = buildBundle(sortNodeEnum, ascending, groupsBefore) - bundle.putBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY, recycleBinBottom) + recycleBinBottom?.let { + bundle.putBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY, recycleBinBottom) + } val fragment = SortDialogFragment() fragment.arguments = bundle return fragment diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt index 7a7cf5acf..f26cc3a70 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt @@ -76,9 +76,6 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen private var specialMode: SpecialMode = SpecialMode.DEFAULT - private var mRecycleBinEnable: Boolean = false - private var mRecycleBin: Group? = null - private var mRecycleViewScrollListener = object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) @@ -102,21 +99,14 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen R.id.menu_sort -> { context?.let { context -> val sortDialogFragment: SortDialogFragment = - if (mRecycleBinEnable) { - SortDialogFragment.getInstance( - PreferencesUtil.getListSort(context), - PreferencesUtil.getAscendingSort(context), - PreferencesUtil.getGroupsBeforeSort(context), + SortDialogFragment.getInstance( + PreferencesUtil.getListSort(context), + PreferencesUtil.getAscendingSort(context), + PreferencesUtil.getGroupsBeforeSort(context), + if (mDatabase?.isRecycleBinEnabled == true) { PreferencesUtil.getRecycleBinBottomSort(context) - ) - } else { - SortDialogFragment.getInstance( - PreferencesUtil.getListSort(context), - PreferencesUtil.getAscendingSort(context), - PreferencesUtil.getGroupsBeforeSort(context) - ) - } - + } else null + ) sortDialogFragment.show(childFragmentManager, "sortDialog") } true @@ -165,9 +155,6 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen } override fun onDatabaseRetrieved(database: ContextualDatabase?) { - mRecycleBinEnable = database?.isRecycleBinEnabled == true - mRecycleBin = database?.recycleBin - context?.let { context -> database?.let { database -> mAdapter = NodesAdapter(context, database).apply { @@ -312,6 +299,11 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen } } + private fun containsRecycleBin(nodes: List): Boolean { + return mDatabase?.isRecycleBinEnabled == true + && nodes.any { it == mDatabase?.recycleBin } + } + fun actionNodesCallback(database: ContextualDatabase, nodes: List, menuListener: NodesActionMenuListener?, @@ -336,8 +328,7 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen // Open and Edit for a single item if (nodes.size == 1) { // Edition - if (database.isReadOnly - || (mRecycleBinEnable && nodes[0] == mRecycleBin)) { + if (database.isReadOnly || containsRecycleBin(nodes)) { menu?.removeItem(R.id.menu_edit) } } else { @@ -357,8 +348,7 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen } // Deletion - if (database.isReadOnly - || (mRecycleBinEnable && nodes.any { it == mRecycleBin })) { + if (database.isReadOnly || containsRecycleBin(nodes)) { menu?.removeItem(R.id.menu_delete) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt index 7c2efbdc6..b58005093 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt @@ -530,9 +530,8 @@ class NodesAdapter ( holder?.otpToken?.apply { text = otpElement?.tokenString setTextSize(mTextSizeUnit, mOtpTokenTextDefaultDimension, mPrefSizeMultiplier) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - textDirection = View.TEXT_DIRECTION_LTR - } + textDirection = View.TEXT_DIRECTION_LTR + } holder?.otpContainer?.setOnClickListener { otpElement?.token?.let { token -> diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.kt b/app/src/main/java/com/kunzisoft/keepass/app/App.kt index b9d76eb05..d3ee6fd8b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.kt +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.kt @@ -38,7 +38,6 @@ class App : MultiDexApplication() { ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleObserver) Stylish.load(this) - PRNGFixes.apply() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/app/PRNGFixes.java b/app/src/main/java/com/kunzisoft/keepass/app/PRNGFixes.java deleted file mode 100644 index 6e168417e..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/app/PRNGFixes.java +++ /dev/null @@ -1,399 +0,0 @@ -package com.kunzisoft.keepass.app; - -/* - * This software is provided 'as-is', without any express or implied - * warranty. In no event will Google be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, as long as the origin is not misrepresented. - */ - -import android.os.Build; -import android.os.Process; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.security.NoSuchAlgorithmException; -import java.security.Provider; -import java.security.SecureRandom; -import java.security.SecureRandomSpi; -import java.security.Security; -import java.util.Locale; - -/** - * Fixes for the output of the default PRNG having low entropy. - * - * The fixes need to be applied via {@link #apply()} before any use of Java - * Cryptography Architecture primitives. A good place to invoke them is in the - * application's {@code onCreate}. - */ -public final class PRNGFixes { - - private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL = - getBuildFingerprintAndDeviceSerial(); - - /** Hidden constructor to prevent instantiation. */ - private PRNGFixes() {} - - /** - * Applies all fixes. - * - * @throws SecurityException if a fix is needed but could not be applied. - */ - public static void apply() { - try { - if (supportedOnThisDevice()) { - applyOpenSSLFix(); - installLinuxPRNGSecureRandom(); - } - } catch (Exception e) { - // Do nothing, do the best we can to implement the workaround - } - } - - private static boolean supportedOnThisDevice() { - // Blacklist on samsung devices - if (Build.MANUFACTURER.toLowerCase(Locale.ENGLISH).contains("samsung")) { - return false; - } - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { - return false; - } - - if (onSELinuxEnforce()) { - return false; - } - - File urandom = new File("/dev/urandom"); - - // Test permissions - if ( !(urandom.canRead() && urandom.canWrite()) ) { - return false; - } - - - // Test actually writing to urandom - try { - FileOutputStream fos = new FileOutputStream(urandom); - fos.write(0); - } catch (Exception e) { - return false; - } - - return true; - - } - - private static boolean onSELinuxEnforce() { - try { - ProcessBuilder builder = new ProcessBuilder("getenforce"); - builder.redirectErrorStream(true); - java.lang.Process process = builder.start(); - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - process.waitFor(); - - String output = reader.readLine(); - - if (output == null) { - return false; - } - - return output.toLowerCase(Locale.US).startsWith("enforcing"); - } catch (Exception e) { - return false; - } - } - - /** - * Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the - * fix is not needed. - * - * @throws SecurityException if the fix is needed but could not be applied. - */ - private static void applyOpenSSLFix() throws SecurityException { - if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) - || (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2)) { - // No need to apply the fix - return; - } - - try { - // Mix in the device- and invocation-specific seed. - Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") - .getMethod("RAND_seed", byte[].class) - .invoke(null, generateSeed()); - - // Mix output of Linux PRNG into OpenSSL's PRNG - int bytesRead = (Integer) Class.forName( - "org.apache.harmony.xnet.provider.jsse.NativeCrypto") - .getMethod("RAND_load_file", String.class, long.class) - .invoke(null, "/dev/urandom", 1024); - if (bytesRead != 1024) { - throw new IOException( - "Unexpected number of bytes read from Linux PRNG: " - + bytesRead); - } - } catch (Exception e) { - throw new SecurityException("Failed to seed OpenSSL PRNG", e); - } - } - - /** - * Installs a Linux PRNG-backed {@code SecureRandom} implementation as the - * default. Does nothing if the implementation is already the default or if - * there is not need to install the implementation. - * - * @throws SecurityException if the fix is needed but could not be applied. - */ - private static void installLinuxPRNGSecureRandom() - throws SecurityException { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { - // No need to apply the fix - return; - } - - // Install a Linux PRNG-based SecureRandom implementation as the - // default, if not yet installed. - Provider[] secureRandomProviders = - Security.getProviders("SecureRandom.SHA1PRNG"); - if ((secureRandomProviders == null) - || (secureRandomProviders.length < 1) - || (!LinuxPRNGSecureRandomProvider.class.equals( - secureRandomProviders[0].getClass()))) { - Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1); - } - - // Assert that new SecureRandom() and - // SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed - // by the Linux PRNG-based SecureRandom implementation. - SecureRandom rng1 = new SecureRandom(); - if (!LinuxPRNGSecureRandomProvider.class.equals( - rng1.getProvider().getClass())) { - throw new SecurityException( - "new SecureRandom() backed by wrong Provider: " - + rng1.getProvider().getClass()); - } - - SecureRandom rng2; - try { - rng2 = SecureRandom.getInstance("SHA1PRNG"); - } catch (NoSuchAlgorithmException e) { - throw new SecurityException("SHA1PRNG not available", e); - } - if (!LinuxPRNGSecureRandomProvider.class.equals( - rng2.getProvider().getClass())) { - throw new SecurityException( - "SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong" - + " Provider: " + rng2.getProvider().getClass()); - } - } - - /** - * {@code Provider} of {@code SecureRandom} engines which pass through - * all requests to the Linux PRNG. - */ - private static class LinuxPRNGSecureRandomProvider extends Provider { - - public LinuxPRNGSecureRandomProvider() { - super("LinuxPRNG", - 1.0, - "A Linux-specific random number provider that uses" - + " /dev/urandom"); - // Although /dev/urandom is not a SHA-1 PRNG, some apps - // explicitly request a SHA1PRNG SecureRandom and we thus need to - // prevent them from getting the default implementation whose output - // may have low entropy. - put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName()); - put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); - } - } - - /** - * {@link SecureRandomSpi} which passes all requests to the Linux PRNG - * ({@code /dev/urandom}). - */ - public static class LinuxPRNGSecureRandom extends SecureRandomSpi { - - /* - * IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed - * are passed through to the Linux PRNG (/dev/urandom). Instances of - * this class seed themselves by mixing in the current time, PID, UID, - * build fingerprint, and hardware serial number (where available) into - * Linux PRNG. - * - * Concurrency: Read requests to the underlying Linux PRNG are - * serialized (on sLock) to ensure that multiple threads do not get - * duplicated PRNG output. - */ - - private static final File URANDOM_FILE = new File("/dev/urandom"); - - - private static final Object sLock = new Object(); - - /** - * Input stream for reading from Linux PRNG or {@code null} if not yet - * opened. - * - * @GuardedBy("sLock") - */ - private static DataInputStream sUrandomIn; - - /** - * Output stream for writing to Linux PRNG or {@code null} if not yet - * opened. - * - * @GuardedBy("sLock") - */ - private static OutputStream sUrandomOut; - - /** - * Whether this engine instance has been seeded. This is needed because - * each instance needs to seed itself if the client does not explicitly - * seed it. - */ - private boolean mSeeded; - - @Override - protected void engineSetSeed(byte[] bytes) { - try { - OutputStream out; - synchronized (sLock) { - out = getUrandomOutputStream(); - } - out.write(bytes); - out.flush(); - mSeeded = true; - } catch (IOException e) { - throw new SecurityException( - "Failed to mix seed into " + URANDOM_FILE, e); - } - } - - @Override - protected void engineNextBytes(byte[] bytes) { - if (!mSeeded) { - // Mix in the device- and invocation-specific seed. - engineSetSeed(generateSeed()); - } - - try { - DataInputStream in; - synchronized (sLock) { - in = getUrandomInputStream(); - } - synchronized (in) { - in.readFully(bytes); - } - } catch (IOException e) { - throw new SecurityException( - "Failed to read from " + URANDOM_FILE, e); - } - } - - @Override - protected byte[] engineGenerateSeed(int size) { - byte[] seed = new byte[size]; - engineNextBytes(seed); - return seed; - } - - private DataInputStream getUrandomInputStream() { - synchronized (sLock) { - if (sUrandomIn == null) { - // NOTE: Consider inserting a BufferedInputStream between - // DataInputStream and FileInputStream if you need higher - // PRNG output performance and can live with future PRNG - // output being pulled into this process prematurely. - try { - sUrandomIn = new DataInputStream( - new FileInputStream(URANDOM_FILE)); - } catch (IOException e) { - throw new SecurityException("Failed to open " - + URANDOM_FILE + " for reading", e); - } - } - return sUrandomIn; - } - } - - private OutputStream getUrandomOutputStream() { - synchronized (sLock) { - if (sUrandomOut == null) { - try { - sUrandomOut = new FileOutputStream(URANDOM_FILE); - } catch (IOException e) { - throw new SecurityException("Failed to open " - + URANDOM_FILE + " for writing", e); - } - } - return sUrandomOut; - } - } - } - - /** - * Generates a device- and invocation-specific seed to be mixed into the - * Linux PRNG. - */ - private static byte[] generateSeed() { - try { - ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream(); - DataOutputStream seedBufferOut = - new DataOutputStream(seedBuffer); - seedBufferOut.writeLong(System.currentTimeMillis()); - seedBufferOut.writeLong(System.nanoTime()); - seedBufferOut.writeInt(Process.myPid()); - seedBufferOut.writeInt(Process.myUid()); - seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL); - seedBufferOut.close(); - return seedBuffer.toByteArray(); - } catch (IOException e) { - throw new SecurityException("Failed to generate seed", e); - } - } - - /** - * Gets the hardware serial number of this device. - * - * @return serial number or {@code null} if not available. - */ - private static String getDeviceSerialNumber() { - // We're using the Reflection API because Build.SERIAL is only available - // since API Level 9 (Gingerbread, Android 2.3). - try { - return (String) Build.class.getField("SERIAL").get(null); - } catch (Exception ignored) { - return null; - } - } - - private static byte[] getBuildFingerprintAndDeviceSerial() { - StringBuilder result = new StringBuilder(); - String fingerprint = Build.FINGERPRINT; - if (fingerprint != null) { - result.append(fingerprint); - } - String serial = getDeviceSerialNumber(); - if (serial != null) { - result.append(serial); - } - try { - return result.toString().getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("UTF-8 encoding not supported"); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt b/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt index 16d1a190c..117e8b67a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt +++ b/app/src/main/java/com/kunzisoft/keepass/app/database/AppDatabase.kt @@ -26,10 +26,11 @@ import android.content.Context import androidx.room.AutoMigration @Database( - version = 2, + version = 3, entities = [FileDatabaseHistoryEntity::class, CipherDatabaseEntity::class], autoMigrations = [ - AutoMigration (from = 1, to = 2) + AutoMigration (from = 1, to = 2), + AutoMigration (from = 2, to = 3) ] ) abstract class AppDatabase : RoomDatabase() { diff --git a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt index 1818f8f6e..6f649f362 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt +++ b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt @@ -49,6 +49,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) { databaseUri, fileDatabaseHistoryEntity?.keyFileUri?.parseUri(), HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey), + fileDatabaseHistoryEntity?.readOnly, fileDatabaseHistoryEntity?.databaseUri?.decodeUri(), fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""), @@ -99,6 +100,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) { fileDatabaseHistoryEntity.databaseUri.parseUri(), fileDatabaseHistoryEntity.keyFileUri?.parseUri(), HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey), + fileDatabaseHistoryEntity.readOnly, fileDatabaseHistoryEntity.databaseUri.decodeUri(), fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias), fileDatabaseInfo.exists, @@ -147,6 +149,8 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) { ?: "", databaseFileToAddOrUpdate.keyFileUri?.toString(), databaseFileToAddOrUpdate.hardwareKey?.value, + databaseFileToAddOrUpdate.readOnly + ?: fileDatabaseHistoryRetrieve?.readOnly, System.currentTimeMillis() ) @@ -168,6 +172,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) { fileDatabaseHistory.databaseUri.parseUri(), fileDatabaseHistory.keyFileUri?.parseUri(), HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey), + fileDatabaseHistory.readOnly, fileDatabaseHistory.databaseUri.decodeUri(), fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias), fileDatabaseInfo.exists, @@ -195,6 +200,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) { fileDatabaseHistory.databaseUri.parseUri(), fileDatabaseHistory.keyFileUri?.parseUri(), HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey), + fileDatabaseHistory.readOnly, fileDatabaseHistory.databaseUri.decodeUri(), databaseFileToDelete.databaseAlias ) diff --git a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt index 96fd5df08..c406a7468 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryEntity.kt @@ -38,6 +38,9 @@ data class FileDatabaseHistoryEntity( @ColumnInfo(name = "hardware_key") var hardwareKey: String?, + @ColumnInfo(name = "read_only") + var readOnly: Boolean?, + @ColumnInfo(name = "updated") val updated: Long ) { diff --git a/app/src/main/java/com/kunzisoft/keepass/biometric/DeviceUnlockManager.kt b/app/src/main/java/com/kunzisoft/keepass/biometric/DeviceUnlockManager.kt index fa487023c..57aa56ff7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/biometric/DeviceUnlockManager.kt +++ b/app/src/main/java/com/kunzisoft/keepass/biometric/DeviceUnlockManager.kt @@ -380,7 +380,7 @@ class DeviceUnlockManager(private var appContext: Context) { } } -fun deviceUnlockError(error: Exception, context: Context): String { +fun deviceUnlockError(error: Throwable, context: Context): String { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && (error is UnrecoverableKeyException || error is KeyPermanentlyInvalidatedException)) { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt index c4a90cf3c..232b88e17 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt @@ -59,9 +59,9 @@ object SearchHelper { && !searchInfo.containsOnlyNullValues()) { // If search provide results database.createVirtualGroupFromSearchInfo( - searchInfo.toString(), - searchInfo.isASearchByDomain(), - MAX_SEARCH_ENTRY + searchInfoString = searchInfo.toString(), + searchInfoByDomain = searchInfo.isASearchByDomain(), + max = MAX_SEARCH_ENTRY )?.let { searchGroup -> if (searchGroup.numberOfChildEntries > 0) { searchWithoutUI = true diff --git a/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt index f116eaf60..4d7fae28c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt @@ -12,6 +12,7 @@ import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE import android.os.Build import android.os.IBinder +import android.util.Log import android.util.TypedValue import android.widget.Toast import androidx.core.app.NotificationCompat @@ -156,11 +157,21 @@ abstract class NotificationService : Service() { mReset = true } - override fun onDestroy() { + override fun onTimeout(startId: Int, fgsType: Int) { + super.onTimeout(startId, fgsType) + Log.e(javaClass::class.simpleName, "The service took too long to execute") + cancelNotification() + stopSelf() + } + + protected fun cancelNotification() { mTimerJob?.cancel() mTimerJob = null notificationManager?.cancel(notificationId) + } + override fun onDestroy() { + cancelNotification() super.onDestroy() } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index 741b390dd..9bc6ae1c6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -618,12 +618,6 @@ object PreferencesUtil { context.resources.getBoolean(R.bool.allow_no_password_default)) } - fun enableReadOnlyDatabase(context: Context): Boolean { - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - return prefs.getBoolean(context.getString(R.string.enable_read_only_key), - context.resources.getBoolean(R.bool.enable_read_only_default)) - } - fun deletePasswordAfterConnexionAttempt(context: Context): Boolean { val prefs = PreferenceManager.getDefaultSharedPreferences(context) return prefs.getBoolean(context.getString(R.string.delete_entered_password_key), @@ -804,7 +798,6 @@ object PreferencesUtil { 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.enable_keep_screen_on_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.auto_focus_search_key) -> editor.putBoolean(name, value.toBoolean()) diff --git a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt index 2042e315a..a32081606 100644 --- a/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/timeout/TimeoutHelper.kt @@ -65,27 +65,19 @@ object TimeoutHelper { (context.applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager?)?.let { alarmManager -> val triggerTime = System.currentTimeMillis() + timeout Log.d(TAG, "TimeoutHelper start") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - && !alarmManager.canScheduleExactAlarms()) { - alarmManager.set( - AlarmManager.RTC, - triggerTime, - getLockPendingIntent(context) - ) - } else { - alarmManager.setExact( - AlarmManager.RTC, - triggerTime, - getLockPendingIntent(context) - ) - } - } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + && !alarmManager.canScheduleExactAlarms()) { alarmManager.set( AlarmManager.RTC, triggerTime, getLockPendingIntent(context) ) + } else { + alarmManager.setExact( + AlarmManager.RTC, + triggerTime, + getLockPendingIntent(context) + ) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt b/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt index 1373fb531..6244ecbda 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/BroadcastAction.kt @@ -77,27 +77,19 @@ class LockReceiver(private var lockAction: () -> Unit) : BroadcastReceiver() { // Launch the effective action after a small time val first: Long = System.currentTimeMillis() + context.getString(R.string.timeout_screen_off).toLong() (context.getSystemService(ALARM_SERVICE) as AlarmManager?)?.let { alarmManager -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - && !alarmManager.canScheduleExactAlarms()) { - alarmManager.set( - AlarmManager.RTC_WAKEUP, - first, - lockPendingIntent - ) - } else { - alarmManager.setExact( - AlarmManager.RTC_WAKEUP, - first, - lockPendingIntent - ) - } - } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + && !alarmManager.canScheduleExactAlarms()) { alarmManager.set( AlarmManager.RTC_WAKEUP, first, lockPendingIntent ) + } else { + alarmManager.setExact( + AlarmManager.RTC_WAKEUP, + first, + lockPendingIntent + ) } } } else { diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt index 0ddf1e257..c53a29e7d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt @@ -67,55 +67,53 @@ object UriUtil { readOnly: Boolean) { try { // try to persist read and write permissions - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - contentResolver?.apply { - var readPermissionAllowed = false - var writePermissionAllowed = false - // Check current permissions allowed - persistedUriPermissions.find { uriPermission -> - uriPermission.uri == uri - }?.let { uriPermission -> - Log.d(TAG, "Check URI permission : $uriPermission") - if (uriPermission.isReadPermission) { - readPermissionAllowed = true - } - if (uriPermission.isWritePermission) { - writePermissionAllowed = true - } + contentResolver?.apply { + var readPermissionAllowed = false + var writePermissionAllowed = false + // Check current permissions allowed + persistedUriPermissions.find { uriPermission -> + uriPermission.uri == uri + }?.let { uriPermission -> + Log.d(TAG, "Check URI permission : $uriPermission") + if (uriPermission.isReadPermission) { + readPermissionAllowed = true } - - // Release permission - if (release) { - if (writePermissionAllowed) { - Log.d(TAG, "Release write permission : $uri") - val removeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION - releasePersistableUriPermission(uri, removeFlags) - } - if (readPermissionAllowed) { - Log.d(TAG, "Release read permission $uri") - val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION - releasePersistableUriPermission(uri, takeFlags) - } + if (uriPermission.isWritePermission) { + writePermissionAllowed = true } + } - // Take missing permission - if (!readPermissionAllowed) { - Log.d(TAG, "Take read permission $uri") + // Release permission + if (release) { + if (writePermissionAllowed) { + Log.d(TAG, "Release write permission : $uri") + val removeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + releasePersistableUriPermission(uri, removeFlags) + } + if (readPermissionAllowed) { + Log.d(TAG, "Release read permission $uri") val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION - takePersistableUriPermission(uri, takeFlags) + releasePersistableUriPermission(uri, takeFlags) } - if (readOnly) { - if (writePermissionAllowed) { - Log.d(TAG, "Release write permission $uri") - val removeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION - releasePersistableUriPermission(uri, removeFlags) - } - } else { - if (!writePermissionAllowed) { - Log.d(TAG, "Take write permission $uri") - val takeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION - takePersistableUriPermission(uri, takeFlags) - } + } + + // Take missing permission + if (!readPermissionAllowed) { + Log.d(TAG, "Take read permission $uri") + val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION + takePersistableUriPermission(uri, takeFlags) + } + if (readOnly) { + if (writePermissionAllowed) { + Log.d(TAG, "Release write permission $uri") + val removeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + releasePersistableUriPermission(uri, removeFlags) + } + } else { + if (!writePermissionAllowed) { + Log.d(TAG, "Take write permission $uri") + val takeFlags: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + takePersistableUriPermission(uri, takeFlags) } } } @@ -140,42 +138,38 @@ object UriUtil { } fun Context.releaseAllUnnecessaryPermissionUris() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - applicationContext?.let { appContext -> - val fileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(appContext) - fileDatabaseHistoryAction.getDatabaseFileList { databaseFileList -> - val listToNotRemove = mutableListOf() - databaseFileList.forEach { - it.databaseUri?.let { databaseUri -> - listToNotRemove.add(databaseUri) - } - it.keyFileUri?.let { keyFileUri -> - listToNotRemove.add(keyFileUri) - } + applicationContext?.let { appContext -> + val fileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(appContext) + fileDatabaseHistoryAction.getDatabaseFileList { databaseFileList -> + val listToNotRemove = mutableListOf() + databaseFileList.forEach { + it.databaseUri?.let { databaseUri -> + listToNotRemove.add(databaseUri) } - // Remove URI permission for not database files - val resolver = appContext.contentResolver - resolver.persistedUriPermissions.forEach { uriPermission -> - val uri = uriPermission.uri - if (!listToNotRemove.contains(uri)) - resolver.releaseUriPermission(uri) + it.keyFileUri?.let { keyFileUri -> + listToNotRemove.add(keyFileUri) } } + // Remove URI permission for not database files + val resolver = appContext.contentResolver + resolver.persistedUriPermissions.forEach { uriPermission -> + val uri = uriPermission.uri + if (!listToNotRemove.contains(uri)) + resolver.releaseUriPermission(uri) + } } } } fun Intent.getUri(key: String): Uri? { try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - val clipData = this.clipData - if (clipData != null) { - if (clipData.description.label == key) { - if (clipData.itemCount == 1) { - val clipItem = clipData.getItemAt(0) - if (clipItem != null) { - return clipItem.uri - } + val clipData = this.clipData + if (clipData != null) { + if (clipData.description.label == key) { + if (clipData.itemCount == 1) { + val clipItem = clipData.getItemAt(0) + if (clipItem != null) { + return clipItem.uri } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt index 0b6b26ae3..ba0394eca 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/PasswordTextEditFieldView.kt @@ -20,7 +20,6 @@ package com.kunzisoft.keepass.view import android.content.Context -import android.os.Build import android.text.Spannable import android.util.AttributeSet import android.util.TypedValue @@ -104,18 +103,14 @@ class PasswordTextEditFieldView @JvmOverloads constructor(context: Context, id = passwordProgressViewId layoutParams = (layoutParams as LayoutParams?)?.also { it.addRule(LEFT_OF, actionImageButtonId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(START_OF, actionImageButtonId) - } + it.addRule(START_OF, actionImageButtonId) } } mPasswordEntropyView.apply { id = passwordEntropyViewId layoutParams = (layoutParams as LayoutParams?)?.also { it.addRule(ALIGN_RIGHT, passwordProgressViewId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(ALIGN_END, passwordProgressViewId) - } + it.addRule(ALIGN_END, passwordProgressViewId) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt index c6909237c..8963534ba 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt @@ -123,13 +123,11 @@ class TemplateEditView @JvmOverloads constructor(context: Context, setMaxChars(templateAttribute.options.getNumberChars()) setMaxLines(templateAttribute.options.getNumberLines()) setActionClick(templateAttribute, field, this) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - if (field.protectedValue.isProtected) { - textDirection = TEXT_DIRECTION_LTR - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO - } + if (field.protectedValue.isProtected) { + textDirection = TEXT_DIRECTION_LTR + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt index d14cc137d..92e4b0140 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt @@ -63,9 +63,7 @@ class TemplateView @JvmOverloads constructor(context: Context, // Here the value is often empty if (field.protectedValue.isProtected) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - textDirection = TEXT_DIRECTION_LTR - } + textDirection = TEXT_DIRECTION_LTR if (mFirstTimeAskAllowCopyProtectedFields) { setCopyButtonState(TextFieldView.ButtonState.DEACTIVATE) setCopyButtonClickListener { _, _ -> @@ -179,9 +177,7 @@ class TemplateView @JvmOverloads constructor(context: Context, otpElement.type.name, ProtectedString(false, otpElement.token))) } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - textDirection = TEXT_DIRECTION_LTR - } + textDirection = TEXT_DIRECTION_LTR mLastOtpTokenView = this mOtpRunnable = Runnable { if (otpElement.shouldRefreshToken()) { diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt index ae5a09e50..486c75b99 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TextEditFieldView.kt @@ -51,9 +51,7 @@ open class TextEditFieldView @JvmOverloads constructor(context: Context, imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO - } + importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO maxLines = 1 } private var actionImageButton = AppCompatImageButton( @@ -70,11 +68,9 @@ open class TextEditFieldView @JvmOverloads constructor(context: Context, resources.displayMetrics ).toInt() it.addRule(ALIGN_PARENT_RIGHT) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(ALIGN_PARENT_END) - } + it.addRule(ALIGN_PARENT_END) } - visibility = View.GONE + visibility = GONE contentDescription = context.getString(R.string.menu_edit) } @@ -91,9 +87,7 @@ open class TextEditFieldView @JvmOverloads constructor(context: Context, id = labelViewId layoutParams = (layoutParams as LayoutParams?)?.also { it.addRule(LEFT_OF, actionImageButtonId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(START_OF, actionImageButtonId) - } + it.addRule(START_OF, actionImageButtonId) } } valueView.apply { @@ -192,7 +186,7 @@ open class TextEditFieldView @JvmOverloads constructor(context: Context, actionImageButton.setImageDrawable(ContextCompat.getDrawable(context, it)) } actionImageButton.setOnClickListener(onActionClickListener) - actionImageButton.visibility = if (onActionClickListener == null) View.GONE else View.VISIBLE + actionImageButton.visibility = if (onActionClickListener == null) GONE else VISIBLE } override var isFieldVisible: Boolean diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt index 6077aa7e5..d9fbf49fa 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt @@ -63,13 +63,12 @@ open class TextFieldView @JvmOverloads constructor(context: Context, 4f, resources.displayMetrics ).toInt() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.marginStart = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - 4f, - resources.displayMetrics - ).toInt() - } + it.marginStart = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 4f, + resources.displayMetrics + ).toInt() + } } protected val valueView = AppCompatTextView(context).apply { @@ -88,13 +87,11 @@ open class TextFieldView @JvmOverloads constructor(context: Context, 8f, resources.displayMetrics ).toInt() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.marginStart = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - 8f, - resources.displayMetrics - ).toInt() - } + it.marginStart = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 8f, + resources.displayMetrics + ).toInt() } setTextIsSelectable(true) } @@ -128,9 +125,7 @@ open class TextFieldView @JvmOverloads constructor(context: Context, id = copyButtonId layoutParams = (layoutParams as LayoutParams?)?.also { it.addRule(ALIGN_PARENT_RIGHT) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(ALIGN_PARENT_END) - } + it.addRule(ALIGN_PARENT_END) } } showButton.apply { @@ -138,14 +133,14 @@ open class TextFieldView @JvmOverloads constructor(context: Context, layoutParams = (layoutParams as LayoutParams?)?.also { if (copyButton.isVisible) { it.addRule(LEFT_OF, copyButtonId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(START_OF, copyButtonId) - } + + it.addRule(START_OF, copyButtonId) + } else { it.addRule(ALIGN_PARENT_RIGHT) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(ALIGN_PARENT_END) - } + + it.addRule(ALIGN_PARENT_END) + } } } @@ -153,18 +148,14 @@ open class TextFieldView @JvmOverloads constructor(context: Context, id = labelViewId layoutParams = (layoutParams as LayoutParams?)?.also { it.addRule(LEFT_OF, showButtonId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(START_OF, showButtonId) - } + it.addRule(START_OF, showButtonId) } } valueView.apply { id = valueViewId layoutParams = (layoutParams as LayoutParams?)?.also { it.addRule(LEFT_OF, showButtonId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(START_OF, showButtonId) - } + it.addRule(START_OF, showButtonId) it.addRule(BELOW, labelViewId) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/TextSelectFieldView.kt b/app/src/main/java/com/kunzisoft/keepass/view/TextSelectFieldView.kt index 781c5dda6..24dbd9ff0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TextSelectFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TextSelectFieldView.kt @@ -10,7 +10,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo -import android.widget.* +import android.widget.AdapterView +import android.widget.BaseAdapter +import android.widget.LinearLayout +import android.widget.RelativeLayout +import android.widget.Spinner +import android.widget.TextView import androidx.annotation.DrawableRes import androidx.appcompat.widget.AppCompatImageButton import androidx.core.content.ContextCompat @@ -51,9 +56,7 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context, imeOptions = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING importantForAutofill = IMPORTANT_FOR_AUTOFILL_NO } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO - } + importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO val drawable = ContextCompat.getDrawable(context, R.drawable.ic_arrow_down_white_24dp) ?.apply { mutate().colorFilter = BlendModeColorFilterCompat @@ -65,14 +68,12 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context, drawable, null ) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - setCompoundDrawablesRelativeWithIntrinsicBounds( - null, - null, - drawable, - null - ) - } + setCompoundDrawablesRelativeWithIntrinsicBounds( + null, + null, + drawable, + null + ) isFocusable = false inputType = InputType.TYPE_NULL maxLines = 1 @@ -94,9 +95,7 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context, resources.displayMetrics ).toInt() it.addRule(ALIGN_PARENT_RIGHT) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it.addRule(ALIGN_PARENT_END) - } + it.addRule(ALIGN_PARENT_END) } visibility = View.GONE contentDescription = context.getString(R.string.menu_edit) @@ -132,18 +131,14 @@ class TextSelectFieldView @JvmOverloads constructor(context: Context, id = labelViewId layoutParams = (layoutParams as LayoutParams?).also { it?.addRule(LEFT_OF, actionImageButtonId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it?.addRule(START_OF, actionImageButtonId) - } + it?.addRule(START_OF, actionImageButtonId) } } valueSpinnerView.apply { id = valueViewId layoutParams = (layoutParams as LayoutParams?).also { it?.addRule(LEFT_OF, actionImageButtonId) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - it?.addRule(START_OF, actionImageButtonId) - } + it?.addRule(START_OF, actionImageButtonId) it?.addRule(BELOW, labelViewId) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt index 3838cff8d..1768c3c49 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt @@ -234,11 +234,7 @@ fun View.updateLockPaddingStart() { R.dimen.hidden_lock_button_size } ).let { lockPadding -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - updatePaddingRelative(lockPadding) - } else { - updatePadding(lockPadding) - } + updatePaddingRelative(lockPadding) } } @@ -308,6 +304,7 @@ fun CollapsingToolbarLayout.changeTitleColor(color: Int) { invalidate() } +@Suppress("DEPRECATION") fun Activity.setTransparentNavigationBar(applyToStatusBar: Boolean = false, applyWindowInsets: () -> Unit) { // Only in portrait if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DeviceUnlockViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DeviceUnlockViewModel.kt index 826debaa8..7a48401dd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DeviceUnlockViewModel.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DeviceUnlockViewModel.kt @@ -20,11 +20,14 @@ import com.kunzisoft.keepass.model.CipherDecryptDatabase import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.model.CredentialStorage import com.kunzisoft.keepass.settings.PreferencesUtil +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import javax.crypto.Cipher @@ -37,6 +40,8 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat private var deviceUnlockManager: DeviceUnlockManager? = null private var databaseUri: Uri? = null + private var mCipherJob: Job? = null + private var deviceUnlockMode = DeviceUnlockMode.BIOMETRIC_UNAVAILABLE var cryptoPrompt: DeviceUnlockCryptoPrompt? = null private set @@ -95,6 +100,18 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat cipherDatabaseAction.registerDatabaseListener(cipherDatabaseListener) } + private fun cancelAndLaunchCipherJob( + coroutineExceptionHandler: CoroutineExceptionHandler = CoroutineExceptionHandler { _, e -> + setException(e) + }, + block: suspend () -> Unit + ) { + mCipherJob?.cancel() + mCipherJob = viewModelScope.launch(coroutineExceptionHandler) { + block() + } + } + fun checkConditionToStoreCredential(condition: Boolean) { isConditionToStoreCredentialVerified = condition checkUnlockAvailability() @@ -153,12 +170,8 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat private fun changeMode(deviceUnlockMode: DeviceUnlockMode) { this.deviceUnlockMode = deviceUnlockMode when (deviceUnlockMode) { - DeviceUnlockMode.STORE_CREDENTIAL -> { - initEncryptData() - } - DeviceUnlockMode.EXTRACT_CREDENTIAL -> { - initDecryptData() - } + DeviceUnlockMode.STORE_CREDENTIAL -> initEncryptData() + DeviceUnlockMode.EXTRACT_CREDENTIAL -> initDecryptData() else -> {} } _uiState.update { currentState -> @@ -246,7 +259,7 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat credential: ByteArray, cipher: Cipher? ) { - try { + cancelAndLaunchCipherJob { deviceUnlockManager?.encryptData( value = credential, cipher = cipher, @@ -260,26 +273,23 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat this.specParameters = ivSpec } ) - } ?: setException(UnknownDatabaseLocationException()) + } ?: throw UnknownDatabaseLocationException() } ) - } catch (e: Exception) { - setException(e) - } finally { - // Reinit credential storage request - _uiState.update { currentState -> - currentState.copy( - credentialRequiredCipher = null - ) - } + } + // Reinit credential storage request + _uiState.update { currentState -> + currentState.copy( + credentialRequiredCipher = null + ) } } fun decryptCredential(cipher: Cipher?) { // retrieve the encrypted value from preferences - databaseUri?.let { databaseUri -> - cipherDatabase?.encryptedValue?.let { encryptedCredential -> - try { + cancelAndLaunchCipherJob { + databaseUri?.let { databaseUri -> + cipherDatabase?.encryptedValue?.let { encryptedCredential -> deviceUnlockManager?.decryptData( encryptedValue = encryptedCredential, cipher = cipher, @@ -295,12 +305,10 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat cipherDatabaseAction.resetCipherParameters(databaseUri) } ) - } catch (e: Exception) { - setException(e) - } - } ?: deleteEncryptedDatabaseKey() - } ?: run { - setException(UnknownDatabaseLocationException()) + } ?: deleteEncryptedDatabaseKey() + } ?: run { + throw UnknownDatabaseLocationException() + } } } @@ -368,7 +376,7 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat } } - fun setException(value: Exception?) { + fun setException(value: Throwable?) { _uiState.update { currentState -> currentState.copy( exception = value @@ -385,29 +393,25 @@ class DeviceUnlockViewModel(application: Application): AndroidViewModel(applicat } private fun initEncryptData() { - try { + cancelAndLaunchCipherJob { deviceUnlockManager = DeviceUnlockManager(getApplication()) deviceUnlockManager?.initEncryptData { cryptoPrompt -> onPromptRequested(cryptoPrompt) - } ?: setException(Exception("Device unlock manager not initialized")) - } catch (e: Exception) { - setException(e) + } ?: throw Exception("Device unlock manager not initialized") } } private fun initDecryptData() { - try { + cancelAndLaunchCipherJob { cipherDatabase?.let { cipherDb -> - deviceUnlockManager = DeviceUnlockManager(getApplication()) - deviceUnlockManager?.initDecryptData(cipherDb.specParameters) { cryptoPrompt -> - onPromptRequested( - cryptoPrompt, - autoOpen = isAutoOpenBiometricPromptAllowed - ) - } ?: setException(Exception("Device unlock manager not initialized")) - } ?: setException(Exception("Cipher database not initialized")) - } catch (e: Exception) { - setException(e) + deviceUnlockManager = DeviceUnlockManager(getApplication()) + deviceUnlockManager?.initDecryptData(cipherDb.specParameters) { cryptoPrompt -> + onPromptRequested( + cryptoPrompt, + autoOpen = isAutoOpenBiometricPromptAllowed + ) + } ?: throw Exception("Device unlock manager not initialized") + } ?: throw Exception("Cipher database not initialized") } } @@ -475,5 +479,5 @@ data class DeviceUnlockState( val cipherDecryptDatabase: CipherDecryptDatabase? = null, val cryptoPromptState: DeviceUnlockPromptMode = DeviceUnlockPromptMode.IDLE_CLOSE, val autoOpenPrompt: Boolean = false, - val exception: Exception? = null + val exception: Throwable? = null ) \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index d956bfc9d..b0211987e 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -19,10 +19,11 @@ --> + android:filterTouchesWhenObscured="true"> + android:filterTouchesWhenObscured="true"> + android:hint="@string/otp_secret" /> @@ -178,8 +177,7 @@ tools:text="30" android:maxLength="3" android:digits="0123456789" - android:imeOptions="actionNext" - tools:targetApi="jelly_bean" /> + android:imeOptions="actionNext" /> + android:imeOptions="actionNext" /> + android:imeOptions="actionNext" /> @@ -237,4 +233,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_breadcrumb.xml b/app/src/main/res/layout/item_breadcrumb.xml index 3fd284c8e..9ca31eeea 100644 --- a/app/src/main/res/layout/item_breadcrumb.xml +++ b/app/src/main/res/layout/item_breadcrumb.xml @@ -65,7 +65,6 @@ android:layout_height="wrap_content" android:src="@drawable/ic_arrow_right_white_24dp" app:tint="?attr/colorOnSurface" - android:importantForAccessibility="no" - tools:targetApi="jelly_bean" /> + android:importantForAccessibility="no" /> diff --git a/app/src/main/res/layout/keyboard_container.xml b/app/src/main/res/layout/keyboard_container.xml index f8a29f13d..51ffc6d77 100644 --- a/app/src/main/res/layout/keyboard_container.xml +++ b/app/src/main/res/layout/keyboard_container.xml @@ -17,10 +17,12 @@ You should have received a copy of the GNU General Public License along with KeePassDX. If not, see . --> -انسخ حقل اقفل قاعدة البيانات أرسل انطباعاتك - \"KeePassDX\" هو تطبيق أندرويد لمدير كلمات السر كي باس \"KeePass\" + تطبيق أندرويد لمدير كلمات السر KeePass. أضف مدخل عدّل مدخل وظيفة اشتقاق المفتاح @@ -213,8 +213,6 @@ إهتزاز عند اللمس صوت عند اللمس اسمح بدون المفتاح الرئيسي - محمي من التعديل - افتح قاعدة البيانات في وضع القراءة افتراضيا تلميحات تعليمية أعد عرض كل المعلومات التعليمية إعادة تعيين الشاشات التلميحات diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index b48c8a1c3..37860b9a6 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -349,7 +349,6 @@ Axtarış məlumatlarını yadda saxla Avtomatik doldurma təklifləri əlavə edildi. Ana açar olmamasına icazə ver - Məlumat bazasını standart olaraq yazma-qorumalı (dəyişməz) aç Məlumat bazasını avtomatik olaraq yadda saxla Bütün təlim məlumatlarını yenidən göstər Təlim ipuclarını sıfırlamaq @@ -572,7 +571,6 @@ Əgər şəxsiyyəti təsdiq edən məlumatlar seçilməyibsə, \"Aç\" düyməsinin sıxılmasına icazə ver Şifrəni sil Məlumat bazasına bağlantı cəhdindən sonra daxil edilmiş şifrəni sil - Yazma qorumalı Hər önəmli prossesdən sonra məlumat bazasını yadda saxla (\"Modifikasiya edilə bilən\" modda keçərlidir) Ekranı açıq saxla Şifrəyə baxarkən və ya redaktə edərkən ekranı açıq saxla @@ -658,4 +656,4 @@ Məlumat bazasını kilidlədikdən sonra avtomatik olaraq əvvəlki klaviaturaya geri dön Özəl sahələr (bölmə) Əvvəlki klaviaturaya geri dön - \ No newline at end of file + diff --git a/app/src/main/res/values-b+sr+Latn/strings.xml b/app/src/main/res/values-b+sr+Latn/strings.xml index c44dee4d7..8c5077340 100644 --- a/app/src/main/res/values-b+sr+Latn/strings.xml +++ b/app/src/main/res/values-b+sr+Latn/strings.xml @@ -413,4 +413,4 @@ Paralelizam Stepen paralelizma (tj. broj niti) koji koristi funkcija izvođenja ključa. Čuvanje baze podataka… - \ No newline at end of file + diff --git a/app/src/main/res/values-b+yue+Hant/strings.xml b/app/src/main/res/values-b+yue+Hant/strings.xml index da7afd41f..8f0a4ff22 100644 --- a/app/src/main/res/values-b+yue+Hant/strings.xml +++ b/app/src/main/res/values-b+yue+Hant/strings.xml @@ -139,4 +139,4 @@ 銀行名稱 網址 用戶名 - \ No newline at end of file + diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml new file mode 100644 index 000000000..1d8597574 --- /dev/null +++ b/app/src/main/res/values-be/strings.xml @@ -0,0 +1,667 @@ + + + Кантакт + Iнфа + Унёсак + Зваротная сувязь + Хатняя старонка + Android рэалізацыя мэнэджара пароляў KeePass. + Прыняць + Дадаць запіс + Рэдагаваць запіс + Дадаць групу + Галоўны ключ + Бяспека + Шыфраванне + Алгарытм шыфравання + Функцыя вывядзення ключа + Тайм-аўт + Час бяздзейнасці перад блакаваннем базы дадзеных + Праграма + Пашыраны ASCII + Каб ствараць, адкрываць і захоўваць файлы базы дадзеных, патрабуецца файлавы мэнэджар, які прымае дзеянні ACTION_CREATE_DOCUMENT і ACTION_OPEN_DOCUMENT. + Дазволіць + Буфер абмену ачышчаны + Памылка буфера абмену + Некаторыя прылады не дазваляюць праграмам выкарыстоўваць буфер абмену. + Не ўдалося ачысціць буфер абмену + Тайм-аўт буфера абмену + Працягласць захоўвання ў буферы абмену (калі падтрымліваецца прыладай) + Адкрыць файл + Дадаць вузел + Дадаць запіс + Дадаць групу + Дадаць элемент + Інфармацыя пра файл + Інфармацыя пра ўліковыя дадзеныя + Інфармацыя пра аднаразовы пароль + Дужкі + Фон + Даччыныя вузлы + Сцяжок пароля + Сцяжок файла ключа + Сцяжок апаратнага ключа + Паўтарыць пераключэнне бачнасці пароля + Значок запісу + Колер базы дадзеных + Колер пярэдняга плану запісу + Колер фону запісу + Загаловак навігацыі + Навігацыйны скрыню адкрыты + Навігацыйны скрыню зачынены + Праверыць + Адхіліць змены? + Адхіліць + Генератар пароляў + Даўжыня пароля + Колькасць слоў парольнай фразы + Дадаць поле + Дадаць укладанне + Выдаліць поле + Абнавіць + Сцерці + Закрыць палі + Выберыце, каб скапіяваць %1$s у буфер абмену + Атрыманне ключа базы дадзеных… + Чаканне запыту выкліку… + Чаканне адказу на выклік… + База дадзеных + Шаблоны + Расшыфроўка змесціва базы дадзеных… + Выкарыстоўваць як базу дадзеных па змаўчанні + Лічбы + KeePassDX © %1$d Kunzisoft з\'яўляецца <strong>праграмай з адкрытым зыходным кодам</strong> і <strong>без рэкламы</strong>. \nПрадастаўляецца «як ёсць», па ліцэнзіі <strong>GPLv3</strong>, без якіх-небудзь гарантый. + <strong>Карыстальніцкія дадзеныя не атрымліваюцца</strong>, гэтая праграма не падключаецца да сервераў, працуе толькі лакальна і цалкам паважае прыватнасць карыстальнікаў. + Каб <strong>захаваць нашу свабоду</strong>, <strong>выпраўляць памылкі</strong>, <strong>дадаваць функцыі</strong> і <strong>заўсёды быць актыўнымі</strong>, мы разлічваем на ваш <strong>уклад</strong>. + Доступ + Адмена + Заўвагі + Пацвердзіць пароль + Створаны + Тэрмін дзеяння + Скончыўся + UUID + Гісторыя + Укладанні + Файл ключа + Апаратны ключ + Зменены + Магчымасць пошуку + Успадкаваць + Аўта-ўвод + Паслядоўнасць аўта-ўводу + Не ўдалося знайсці дадзеныя запісу. + Пароль + Тэгі + Карыстальніцкія дадзеныя + Захаваць + Назва + Наладзіць аднаразовы пароль + Тып OTP + Сакрэт + Перыяд (секунды) + Лічыльнік + Лічбы + Фільтры пошуку + Алгарытм + Цяперашняя група + З улікам рэгістра + Рэгулярны выраз + Дэбетавая / Крэдытная карта + Уладальнік + Нумар + CVV + PIN + Пасведчанне асобы + Назва + Месца выдачы + Дата выдачы + Электронная пошта + Адрас электроннай пошты + Wi-Fi + SSID + Тып + Крыптавалютны кашалёк + Токен + Адкрыты ключ + Закрыты ключ + Насенне + Рахунак + Банк + Назва банка + SWIFT / BIC + IBAN + Бяспечная нататка + Членства + Стандартны + Шаблон + Версія + OTP + URL + Імя карыстальніка + Патокавы шыфр Arcfour не падтрымліваецца. + Не ўдалося апрацаваць гэты URI ў KeePassDX. + Не ўдалося стварыць файл. + Не ўдалося прачытаць базу дадзеных. + Пераканайцеся, што шлях правільны. + Няправільны сакрэт OTP. + Увядзіце імя. + Гэтае слова зарэзервавана і не можа быць выкарыстана. + Выберыце файл ключа. + Выберыце апаратны ключ. + Не хапае памяці для загрузкі ўсёй базы дадзеных. + XML мае няправільны фармат. + Не ўдалося загрузіць базу дадзеных. + Не ўдалося загрузіць ключ. Паспрабуйце знізіць Выкарыстанне памяці KDF. + Павінен быць абраны хаця б адзін тып генерацыі пароляў. + Павінен быць усталяваны хаця б адзін набор уліковых дадзеных. + Паролі не супадаюць. + Колькасць цыклаў пераўтварэння занадта высокая. Устаноўка 2147483648. + Кожны радок павінен мець назву поля. + Гэтая метка ўжо існуе. + Увядзіце станоўчае цэлае лік у полі Даўжыня. + Не ўдалося ўключыць службу аўтазапаўнення. + Вы не можаце перамясціць групу сюды. + Вы не можаце перамясціць запіс сюды. + Вы не можаце скапіяваць запіс сюды. + Вы не можаце скапіяваць групу сюды. + Немагчыма стварыць файл базы дадзеных. + Немагчыма стварыць базу дадзеных з гэтым паролем і файлам ключа. + Не ўдалося захаваць базу дадзеных. + Сакрэтны ключ павінен быць у фармаце Base32. + Даўжыня сакрэтнага ключа павінна быць не менш за %1$d сімвалаў. + Лічыльнік павінен быць паміж %1$d і %2$d. + Перыяд павінен быць ад %1$d да %2$d секунд. + Токен павінен утрымоўваць ад %1$d да %2$d лічбаў. + Існуючы тып OTP не распазнаецца гэтай формай, яго праверка можа больш не генераваць токен правільна. + Гэты тэкст не супадае з запытаным элементам. + Захаванне новага элемента недапушчальна ў базе дадзеных, адкрытай толькі для чытання. + Назва поля ўжо існуе. + Немагчыма атрымаць URI базы дадзеных. + Не ўдалося правільна аднавіць спіс. + Файл, які вы спрабуеце загрузіць, занадта вялікі. + Адбылася памылка пры загрузцы дадзеных файла. + Дадзеныя файла ўжо існуюць. + Адбылася памылка пры выдаленні дадзеных файла. + Адбылася памылка пры выкананні дзеянняў з базай дадзеных. + Выклік ужо запытаны. + Адказнае дзеянне ўжо выканана. + Немагчыма атрымаць адказ на выклік. + Скасавана карыстальнікам. + Патрабуецца драйвер для %1$s. + Немагчыма аб\'яднаць з файламі базы дадзеных kdb. + Месцазнаходжанне базы дадзеных невядома, дзеянне з базай дадзеных не можа быць выканана. + Апаратны ключ не падтрымліваецца. + Ключ не можа быць пустым. + Назва поля + Значэнне поля + Не ўдалося знайсці файл. Паспрабуйце зноў адкрыць яго з файлавага мэнэджара. + Пашкоджаны файл. + Файлавы мэнэджар + Стварыць пароль + Стварыць файл ключа + Пацвердзіць пароль + Згенераваны пароль + Назва групы + Назва значка + Файл ключа + Даўжыня + Пароль + Пароль + Парольная фраза + Не ўдалося прачытаць уліковыя дадзеныя. + Няправільны алгарытм. + %1$s з такім жа UUID %2$s ужо існуе. + Не ўдалося распазнаць фармат базы дадзеных. + Файл ключа пусты. + Даўжыня + Вузлы + Схаваць паролі + Маскіраваць паролі (***) па змаўчанні + Размаляваць паролі + Размаляваць сімвалы пароля па тыпу + Паказаць імёны карыстальнікаў + Адлюстроўвае імёны карыстальнікаў у спісах запісаў + Паказаць колькасць запісаў + Адлюстроўвае колькасць запісаў у групе + Рэкурсіўная колькасць запісаў + Рэкурсіўна падлічвае колькасць запісаў у групе + Паказаць токен OTP + Адлюстроўвае токены OTP у спісе запісаў + Паказаць UUID + Адлюстроўвае UUID, прывязаны да запісу або групы + Памер элементаў спісу + Памер тэксту ў спісе элементаў + Стварэнне базы дадзеных… + Загрузка базы дадзеных… + Ніжні рэгістр + Аб праграме + Змяніць галоўны ключ + Копія %1$s + Налады + Налады праграмы + Пошук, блакаванне, гісторыя, уласцівасці + Запаўненне формы + Клавіятура, аўтазапаўненне, буфер абмену + Разблакіроўка прылады + Біяметрыя, уліковыя дадзеныя прылады + Налады базы дадзеных + Метададзеныя, смеццевая скрыня, шаблоны, гісторыя + Налады бяспекі + Шыфраванне, функцыя вывядзення ключа + Налады галоўнага ключа + Змена, аднаўленне + Ахвяраваць + Рэдагаваць + Капіяваць + Перамясціць + Уставіць + Выдаліць + Адмена + Схаваць пароль + Заблакаваць базу дадзеных + Захаваць дадзеныя + Аб\'яднаць дадзеныя + Перазагрузіць дадзеныя + Аб\'яднаць з… + Захаваць копію ў… + Адкрыць + Пошук + Паказаць пароль + Выдаліць ключ разблакіроўкі прылады + Перайсці па URL + Абаронены ад запісу + З магчымасцю змены + Ачысціць смеццевую скрыню + Аднавіць гісторыю + Выдаліць гісторыю + Знешні значок + Мінус + Ніколі + Няма вынікаў пошуку + Усталюйце вэб-браўзер, каб адкрыць гэты URL. + Адкрыць існуючае сховішча + Стварыць новае сховішча + Хуткі пошук + Запытаць пошук пры адкрыцці базы дадзеных + Пошук па паддаменах + Пошук вэб-даменаў з абмежаваннямі паддаменаў + Стварэнне новай базы дадзеных… + Працую… + Абарона + Абаронены ад запісу + У залежнасці ад вашага файлавага мэнэджара, KeePassDX можа не мець дазволу на запіс у сховішча. + База дадзеных змяшчае дублікаты UUID. + Вырашыць праблему шляхам генерацыі новых UUID для дублікатаў, каб працягнуць? + Рэжым пошуку + Рэжым захавання + Рэжым выбару + Рэжым рэгістрацыі + Запамінаць размяшчэнне баз дадзеных + Адсочвае, дзе захоўваюцца базы дадзеных + Запамінаць размяшчэнне файлаў ключоў + Адсочвае, дзе захоўваюцца файлы ключоў + Запамінаць апаратныя ключы + Адсочвае выкарыстаныя апаратныя ключы + Паказваць апошнія файлы + Паказваць размяшчэнне нядаўніх баз дадзеных + Схаваць зламаныя спасылкі на базы дадзеных + Схаваць зламаныя спасылкі ў спісе нядаўніх баз дадзеных + Імпартаваць налады праграмы + Выберыце файл для імпарту налад праграмы + Экспартаваць налады праграмы + Стварыць файл для экспарту налад праграмы + Уласцівасці KeePassDX для кіравання наладамі праграмы + Налады праграмы імпартаваны + Памылка падчас імпарту налад праграмы. + Налады праграмы экспартаваны + Памылка падчас экспарту налад праграмы. + Корань + Алгарытм шыфравання базы дадзеных, які выкарыстоўваецца для ўсіх дадзеных + Каб стварыць ключ для алгарытму шыфравання, галоўны ключ пераўтворыцца з дапамогай выпадкова саленай функцыі вывядзення ключа. + Цыклы пераўтварэння + Дадатковыя цыклы шыфравання забяспечваюць больш высокую абарону ад нападаў грубай сілы, але могуць моцна запаволіць загрузку і захаванне. + Выкарыстанне памяці + Аб\'ём памяці, які будзе выкарыстоўвацца функцыяй вывядзення ключа. + Паралелізм + Ступень паралелізму (г.зн. колькасць патокаў), выкарыстоўваных функцыяй вывядзення ключа. + Захаванне базы дадзеных… + Выкананне каманды… + Не зачыняйце праграму… + Прабел + Фільтр + Сартаваць + Спачатку ніжэйшыя ↓ + Групы спачатку + Смеццевая скрыня ўнізе + Натуральны парадак + Назва + Імя карыстальніка + Стварэнне + Змена + Доступ + Спецыяльныя + Пошук + Падкрэсліванне + Непадтрымліваемая версія базы дадзеных. + ВЕРХНІ РЭГІСТР + Папярэджанне + Пазбягайце сімвалаў пароляў па-за межамі фармату кадоўкі тэксту ў файле базы дадзеных (нераспазнаныя сімвалы пераўтвараюцца ў адну і тую ж літару). + Дайце дазвол на запіс у файл, каб захаваць змены ў базе дадзеных + Доступ да файла скасаваны файлавым мэнэджарам + База дадзеных ужо адкрыта, спачатку зачыніце яе, каб адкрыць новую + Працягнуць без абароны блакаваннем паролем? + Працягнуць без ключа шыфравання? + Беззваротна выдаліць выбраныя вузлы? + Беззваротна выдаліць усе вузлы са смеццевай скрыні? + База дадзеных KeePass павінна ўтрымліваць толькі невялікія службовыя файлы (напрыклад, файлы PGP-ключа).\n\nВаша база дадзеных можа стаць вельмі вялікай і знізіць прадукцыйнасць пасля гэтай загрузкі. + Загрузка гэтага файла заменіць існуючы. + Усё роўна дадаць файл? + Выдаленне незвязаных дадзеных можа паменшыць памер вашай базы дадзеных, але таксама можа выдаліць дадзеныя, якія выкарыстоўваюцца для плагінаў KeePass. + Усё роўна выдаліць гэтыя дадзеныя? + Не рэкамендуецца дадаваць пусты файл ключа. + Не рэкамендуецца дадаваць вялікі файл ключа, гэта можа перашкодзіць адкрыццю базы дадзеных. + Змесціва файла ключа ніколі не павінна змяняцца, і ў лепшым выпадку, павінна ўтрымліваць выпадкова згенераваныя дадзеныя. + Інфармацыя, якая змяшчаецца ў файле базы дадзеных, была зменена па-за межамі праграмы. + Аб\'яднаць дадзеныя, захаваць базу дадзеных з перазапісам знешніх мадыфікацый або перазагрузіць яе з апошнімі зменамі. + Перазагрузіць базу дадзеных з апошнімі зменамі. + Перазагрузка базы дадзеных выдаліць лакальна змененыя дадзеныя. + Доступ да файла скасаваны файлавым мэнэджарам, зачыніце базу дадзеных і зноў адкрыйце яе з яе месцазнаходжання. + Вы не дазволілі праграме выкарыстоўваць дакладны таймер. У выніку функцыі, якія патрабуюць таймера, не будуць выкананы з дакладным часам. + Цэласнасць файла не гарантуецца, бо Android можа змяняць яго дадзеныя на хаду. Зменіце пашырэнне файла на .bin для правільнай цэласнасці. + Дазвол на апавяшчэнні дазваляе адлюстроўваць статус базы дадзеных і блакаваць яе з дапамогай лёгкадаступнай кнопкі.\n\nКалі вы не актывуеце гэты дазвол, база дадзеных, адкрытая ў фонавым рэжыме, не будзе бачная, калі на пярэднім плане знаходзіцца іншая праграма. + Дазвол на апавяшчэнні неабходны для выкарыстання функцыі апавяшчэння аб буферы абмену. + Пазней + Спытаць + Аб\'яднанне паспяхова завершана + Дазвол + Версія %1$s + Зборка %1$s + Наладзіць + Біяметрычныя або ўліковыя дадзеныя прылады не зарэгістраваны. + Патрабуецца абнаўленне біяметрычнай бяспекі. + Сховішча ключоў неналежным чынам ініцыялізавана. + Спасылка на разблакіроўку прылады + Спасылка на разблакіроўку прылады + Вам усё роўна трэба памятаць асноўныя ўліковыя дадзеныя сховішча, калі вы выкарыстоўваеце распазнаванне разблакіроўкі прылады. + Распазнаванне разблакіроўкі прылады + Выняць уліковыя дадзеныя базы дадзеных з дапамогай дадзеных разблакіроўкі прылады + Зашыфраваны пароль захаваны + Немагчыма прачытаць ключ разблакіроўкі прылады. Выдаліце яго і паўтарыце працэдуру распазнавання разблакіроўкі. + Не ўдалося распазнаць адбітак разблакіроўкі прылады + Недаступна + Немагчыма ініцыялізаваць запыт разблакіроўкі прылады. + Увядзіце пароль, а затым націсніце гэтую кнопку. + Гісторыя + Уласцівасці + Знешні выгляд + Тэмы, колеры, атрыбуты + Біяметрычны + Уліковыя дадзеныя прылады + Агульнае + Аўтазапаўненне + Аўтазапаўненне формы KeePassDX + Увайсці з дапамогай KeePassDX + Уключыце аўтазапаўненне, каб хутка запаўняць формы ў іншых праграмах + Выбраць запіс… + Усталяваць службу аўтазапаўнення па змаўчанні + Налады аўтазапаўнення + Памер згенераванага пароля + Усталёўвае памер згенераваных пароляў па змаўчанні + Сімвалы пароля + Усталяваць дазволеныя сімвалы генератара пароляў + База дадзеных адкрыта + Буфер абмену + Капіюйце палі запісу, выкарыстоўваючы буфер абмену вашай прылады + Апавяшчэнні буфера абмену + Паказваць апавяшчэнні буфера абмену, каб капіяваць палі пры праглядзе запісу + Калі аўтаматычнае выдаленне з буфера абмену не ўдаецца, выдаліце яго гісторыю ўручную. + Блакаванне + Блакаванне экрана + Блакаваць базу дадзеных праз некалькі секунд пасля выключэння экрана + Націснуць «Назад», каб заблакаваць + Блакаваць базу дадзеных, калі карыстальнік націскае кнопку «Назад» на галоўным экране + Паказаць кнопку блакавання + Адлюстроўвае кнопку блакавання ў карыстальніцкім інтэрфейсе + Змесціва + Разблакіраваць + Разблакіроўка прылады + Націсніце, каб выдаліць ключы разблакіроўкі прылады + Выкарыстоўвайце разблакіроўку прылады, каб лягчэй адкрываць базу дадзеных + Біяметрычная разблакіроўка + Дазваляе сканаваць біяметрыю для адкрыцця базы дадзеных + Разблакіроўка з дапамогай уліковых дадзеных прылады + Дазваляе выкарыстоўваць уліковыя дадзеныя прылады для адкрыцця базы дадзеных + Запыт на аўтаматычнае адкрыццё + Аўтаматычна запытваць разблакіроўку прылады, калі база дадзеных наладжана на яе выкарыстанне + Часовая разблакіроўка прылады + Не захоўваць зашыфраванае змесціва для выкарыстання разблакіроўкі прылады + Тэрмін дзеяння разблакіроўкі прылады + Працягласць выкарыстання разблакіроўкі прылады перад выдаленнем яе змесціва + Тайм-аўт разблакіроўкі прылады + Выдаліць ключы шыфравання + Выдаліць усе ключы шыфравання, звязаныя з распазнаваннем разблакіроўкі прылады + Выдаліць усе ключы шыфравання, звязаныя з распазнаваннем разблакіроўкі прылады? + Гэтая функцыя будзе захоўваць зашыфраваныя дадзеныя ўліковых дадзеных у бяспечным Keystore вашай прылады.\n\nУ залежнасці ад рэалізацыі натыўнага API аперацыйнай сістэмы, яна можа быць не цалкам функцыянальнай.\n\nПраверце сумяшчальнасць і бяспеку Keystore з вытворцам вашай прылады і стваральнікам ПЗ, якое вы выкарыстоўваеце. + Не ўдалося запусціць гэтую функцыю. + Прылада працуе на Android %1$s, але патрабуецца %2$s або больш позняя версія. + Не ўдалося знайсці адпаведнае апаратнае забеспячэнне. + Імя файла + Шлях + Прызначыць галоўны ключ + Дадзеныя + Сцісканне дадзеных + Сцісканне дадзеных памяншае памер базы дадзеных + Выдаліць незвязаныя дадзеныя + Выдаляе ўкладанні, якія змяшчаюцца ў базе дадзеных, але не звязаны з запісам + Выкарыстанне смеццевай скрыні + Перамяшчае групы і запісы ў групу Смеццевая скрыня перад выдаленнем + Група смеццевай скрыні + Выкарыстанне шаблонаў + Выкарыстоўвайце дынамічныя шаблоны для запаўнення палёў запісу + Група шаблонаў + Максімальная колькасць + Абмежаваць колькасць элементаў гісторыі на запіс + Максімальны памер + Абмежаваць памер гісторыі на запіс + Рэкамендаваць абнаўленне + Рэкамендаваць змену галоўнага ключа (дні) + Прымусовае абнаўленне + Патрабаваць змены галоўнага ключа (дні) + Прымусовае абнаўленне наступным разам + Патрабаваць змены галоўнага ключа ў наступны раз (адзін раз) + Шрыфт поля + Змяніць шрыфт, які выкарыстоўваецца ў палях, для лепшай бачнасці сімвалаў + Давер буфера абмену + Дазволіць капіяванне пароля запісу і абароненых палёў у буфер абмену + Папярэджанне: Буфер абмену выкарыстоўваецца ўсімі праграмамі. Калі капіююцца канфідэнцыйныя дадзеныя, іншае праграмнае забеспячэнне можа аднавіць іх. + Уключыць + Адключыць + Апавяшчэнне + Ачысціць пры закрыцці + Блакаваць базу дадзеных пасля заканчэння тэрміну дзеяння буфера абмену або закрыцця апавяшчэння пасля пачатку выкарыстання + Назва базы дадзеных + Апісанне базы дадзеных + Імя карыстальніка па змаўчанні + Карыстальніцкі колер базы дадзеных + Версія базы дадзеных + Тэкст + Інтэрфейс + Іншае + Сцісканне + Няма + Gzip + Смеццевая скрыня + Шаблоны + Клавіятура + Магічная клавіятура + Актываваць карыстальніцкую клавіятуру, якая запаўняе вашыя паролі і ўсе палі ідэнтычнасці + Налады клавіятуры прылады + Магічная клавіятура + Магічная клавіятура (KeePassDX) + Налады магічнай клавіятуры + Запіс + Выбар запісу + Пры праглядзе запісу ў KeePassDX, запоўніце магічную клавіятуру гэтым запісам + Інфармацыя аб апавяшчэннях + Паказваць апавяшчэнне, калі запіс даступны + Захаваць агульную інфармацыю + Паспрабаваць захаваць агульную інфармацыю пры ручным выбары запісу для палягчэння будучага выкарыстання + Ачысціць пры закрыцці + Зачыніць базу дадзеных пры закрыцці апавяшчэння + Тайм-аўт + Тайм-аўт для ачысткі запісу клавіятуры + Запіс + %1$s даступны на магічнай клавіятуры + %1$s + Знешні выгляд + Тэма клавіятуры + Клавішы + Аўтаматычнае дзеянне клавішы + Дзеянне клавішы Go пасля націску на клавішу Поле + Вібрацыйныя націскі клавіш + Гукавыя націскі клавіш + Змяніць клавіятуру + Экран уліковых дадзеных базы дадзеных + Аўтаматычна пераключацца назад на папярэднюю клавіятуру на экране ўліковых дадзеных базы дадзеных + Экран пошуку + Аўтаматычна пераключацца назад на папярэднюю клавіятуру на экране пошуку + Пераключыцца назад + Аўтаматычна пераключацца назад на папярэднюю клавіятуру пасля выканання Аўтаматычнага дзеяння клавішы + Заблакаваць базу дадзеных + Аўтаматычна пераключацца назад на папярэднюю клавіятуру пасля блакавання базы дадзеных + Карыстальніцкія палі + Вярнуцца да папярэдняй клавіятуры + Выбраць запіс + Backspace + Enter + Зачыніць базу дадзеных + Зачыніць базу дадзеных пасля выбару аўтазапаўнення + Убудаваныя прапановы + Спрабаваць адлюстраваць прапановы аўтазапаўнення непасрэдна з сумяшчальнай клавіятуры + Ручной выбар + Адлюстраваць опцыю, каб дазволіць карыстальніку выбраць запіс базы дадзеных + Захаваць інфармацыю пошуку + Спрабаваць захаваць інфармацыю пошуку пры ручным выбары запісу для палягчэння будучага выкарыстання + Спытаць пра захаванне дадзеных + Спытаць пра захаванне дадзеных пасля завяршэння запаўнення формы + Чорны спіс праграм + Чорны спіс, які перашкаджае аўтазапаўненню праграм + Чорны спіс вэб-даменаў + Чорны спіс, які перашкаджае аўтазапаўненню вэб-даменаў + Заблакаваць аўтазапаўненне + Перазапусціце праграму, якая змяшчае форму, каб актываваць блакаванне. + Захаванне дадзеных недапушчальна для базы дадзеных, адкрытай толькі для чытання. + Прапановы аўтазапаўнення дададзены. + Дазволіць без галоўнага ключа + Дазваляе націснуць кнопку Адкрыць, калі не выбраны ўліковыя дадзеныя + Выдаліць пароль + Выдаляе ўведзены пароль пасля спробы падключэння да базы дадзеных + Аўтазахаванне базы дадзеных + Захоўваць базу дадзеных пасля кожнага важнага дзеяння (у рэжыме З магчымасцю змены) + Трымаць экран уключаным + Трымаць экран уключаным пры праглядзе або рэдагаванні запісу + Рэжым скрыншота + Дазволіць староннім праграмам запісваць або рабіць скрыншоты праграмы + Навучальныя падказкі + Вылучыць элементы, каб даведацца, як працуе праграма + Скінуць навучальныя падказкі + Паказаць усю навучальную інфармацыю зноўку + Навучальныя падказкі скінуты + Стварыце свой файл базы дадзеных + Стварыце свой першы файл кіравання паролямі. + Адкрыць існуючую базу дадзеных + Адкрыйце свой папярэдні файл базы дадзеных з файлавага браўзера, каб працягнуць выкарыстоўваць яго. + Дадаць элементы ў вашу базу дадзеных + Запісы дапамагаюць кіраваць лічбавымі ідэнтыфікацыйнымі дадзенымі.\n\nГрупы (~тэчкі) арганізуюць запісы ў вашай базе дадзеных. + Пошук па запісах + Увядзіце назву, імя карыстальніка або змесціва іншых палёў, каб знайсці свае паролі. + Разблакіроўка базы дадзеных прылады + Прывяжыце свой пароль да адсканаванай біяметрыі або ўліковых дадзеных прылады, каб хутка разблакаваць базу дадзеных. + Рэдагаваць гэты запіс + Рэдагуйце свой запіс з дапамогай карыстальніцкіх палёў. Дадзеныя пула могуць спасылацца паміж рознымі палямі запісаў. + Стварыць надзейны пароль + Стварыце надзейны пароль, каб звязаць яго са сваім запісам, лёгка вызначыце яго ў адпаведнасці з крытэрыямі формы і не забудзьцеся пра бяспечны пароль. + Праверыць запіс + Не забудзьцеся праверыць свой запіс і захаваць базу дадзеных.\n\nКалі актывавана аўтаматычнае блакаванне і вы забыліся, што рабілі змены, вы рызыкуеце страціць свае дадзеныя. + Дадаць карыстальніцкія палі + Зарэгіструйце дадатковае поле, дадайце значэнне і пры жаданні абароніце яго. + Дадаць укладанне + Загрузіце ўкладанне ў свой запіс, каб захаваць важныя знешнія дадзеныя. + Наладзіць OTP + Наладзьце кіраванне аднаразовым паролем (HOTP / TOTP) для стварэння токена, які запытваецца для двухфактарнай аўтэнтыфікацыі (2FA). + Разблакаваць вашу базу дадзеных + Увядзіце пароль і/або файл ключа, каб разблакаваць базу дадзеных.\n\nСтварыце рэзервовую копію файла базы дадзеных у бяспечным месцы пасля кожнай змены. + Абараніць вашу базу дадзеных ад запісу + Змяніць рэжым адкрыцця для сеансу.\n\n\"Абаронены ад запісу\"- прадухіляе ненаўмысныя змены ў базе дадзеных.\n\"Мадыкафаны\"- дазваляе вам дадаваць, выдаляць або змяняць усе элементы, як пажадаеце. + Скапіяваць поле + Скапіяваныя палі можна ўставіць у любым месцы.\n\nВыкарыстоўвайце метад запаўнення формы, які вам больш падабаецца. + Заблакаваць базу дадзеных + Хутка заблакаваць базу дадзеных, вы можаце наладзіць праграму, каб яна блакавалася праз некаторы час і калі экран выключаецца. + Сартаванне элементаў + Выберыце, як сартаваць запісы і групы. + Удзельнічаць + Дапамажыце павысіць стабільнасць, бяспеку і дадаць больш функцый. + У адрозненне ад многіх праграм для кіравання паролямі, гэтая <strong>без рэкламы</strong>, <strong>копілефтнае свабоднае праграмнае забеспячэнне</strong> і не збірае персанальныя дадзеныя на сваіх серверах, незалежна ад версіі, якой вы карыстаецеся. + Купляючы pro-версію, вы атрымаеце доступ да гэтага <strong>візуальнага стылю</strong> і асабліва дапаможаце <strong>рэалізацыі грамадскіх праектаў.</strong> + Гэты <strong>візуальны стыль</strong> даступны дзякуючы вашай шчодрасці. + <strong>Унёсшы ўклад</strong> у праект <i>(матэрыяльна, кодам, перакладам)</i>, вы дапаможаце яму працягваць жыць і развівацца, а таксама будзеце мець права на працэдуру разблакіроўкі <strong>тэмы</strong>. + Гэтая функцыя <strong>знаходзіцца ў стадыі распрацоўкі</strong> і патрабуе вашага <strong>ўкладу</strong>, каб неўзабаве стаць даступнай. + Купляючы <strong>pro-версію</strong>, + <strong>Унёсшы ўклад</strong>, + вы заахвочваеце распрацоўшчыкаў ствараць <strong>новыя функцыі</strong> і <strong>выпраўляць памылкі</strong> ў адпаведнасці з вашымі заўвагамі. + Вялікі дзякуй за ваш уклад. + Мы прыкладаем усе намаганні, каб хутка выпусціць гэтую функцыю. + Не забудзьцеся абнаўляць праграму, усталёўваючы новыя версіі. + Спампаваць + Унесці ўклад + Спампаваць %1$s + Загрузіць %1$s + Ініцыялізацыя… + Выконваецца: %1$d%% + Завяршэнне… + Гатова! + Скасавана! + Б + КіБ + МіБ + ГіБ + Энтрапія: %1$s біт + Энтрапія: Высокая + Энтрапія: Разлік… + Хаця б адзін сімвал з кожнай + Выключыць неадназначныя сімвалы + Разглядаць сімвалы + Падзяляльнік слоў + Ігнараваць сімвалы + ніжні рэгістр + ВЕРХНІ РЭГІСТР + Рэгістр загалоўкаў + Колькасць сімвалаў: %1$d + Рэжым скрыншота + Тэма праграмы + Тэма, якая выкарыстоўваецца ў праграме + Лес + Боскі + Класічны + Просты + Месяц + Сонца + Адказ + Кунцыт + Па сістэме + Яркасць тэмы + Выбраць светлыя або цёмныя тэмы + Светлая + Цёмная + Стандартны + Карыстальніцкі + Пакет значкоў + Пакет значкоў, які выкарыстоўваецца ў праграме + Колеры запісаў + Адлюстроўвае колеры пярэдняга плану і фону для запісу + Схаваць запісы з мінулым тэрмінам дзеяння + Запісы з мінулым тэрмінам дзеяння не паказваюцца + Схаваць шаблоны + Шаблоны не паказваюцца + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 41cb7e4ad..2f05ac045 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -22,7 +22,7 @@ Продължителност на съхранение в клипборда (ако се поддържа от вашето устройство) Някои устройства не позволяват на приложенията да използват клипборда. Заглавие - Версия за Андроид на приложението за управление на пароли KeePass + Разработка за Андроид на приложението за управление на пароли KeePass. Запазване Потвърждаване на парола Функция за извличане на ключ @@ -352,7 +352,6 @@ Настройки на Magikeyboard Избор на записи Разрешаване без главна парола - Само за четене %1$s Записът %1$s е достъпен в Magikeyboard Запис @@ -402,7 +401,6 @@ Бутон за заключване Включете услугата за попълване на формуляри в други приложения Свойства - По подразбиране отваря хранилището само за четене Не забравяйте да потвърдите записа и да го запазите в хранилището. \n \nАко се задейства автоматичното заключване, а сте направили промени, рискувате загуба на данни. diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index dd388e5a1..3817af94d 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -90,4 +90,4 @@ নিম্নরেখা প্রবেশ অনুসন্ধান - \ No newline at end of file + diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index fa2887728..3a9c202d5 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -471,8 +471,6 @@ Metadades, paperera de reciclatge, plantilles, historial Algorisme de xifratge de la base de dades utilitzat per a totes les dades Suprimeix la contrasenya introduïda després d\'un intent de connexió a una base de dades - Protegit contra l\'escriptura - Obre la base de dades en mode només de lectura per defecte Permet que les aplicacions de tercers gravin o facin captures de pantalla de l\'aplicació Ressalta els elements per saber com funciona l\'aplicació Reinicia els consells educatius diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 91bb6d430..fa58b14e6 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -19,7 +19,7 @@ Czech translation by Jan Vaněk --> Domovská stránka - Implementace správce hesel KeePass pro Android + Implementace správce hesel KeePass pro Android. Přijmout Přidat záznam Přidat skupinu @@ -224,8 +224,6 @@ Aktivovat vlastní klávesnici, která snadno vyplní hesla a další položky identity Umožnit bez hlavního klíče Povolit klepnutí na \"Otevřít\", i když není vybráno žádné heslo - Chráněno před zápisem - Ve výchozím stavu otevřít databázi pouze pro čtení Vzdělávací nápovědy Zvýraznit prvky k pochopení práce s aplikací Nastavit vzdělávací nápovědy do výchozího stavu diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 0639d64a5..64fbac849 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -223,8 +223,6 @@ Aktiver et brugerdefineret tastatur, der udfylder adgangskoder og alle identitetsfelter Tillad ingen hovednøgle Tillader at trykke på knappen \"Åbn\", hvis der ikke er valgt nogen legitimationsoplysninger - Skrivebeskyttet - Åbn som standard databasen skrivebeskyttet Praktiske tips Fremhæv elementer for at lære, hvordan appen fungerer Nulstil praktiske tips diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 360035ae6..1e487a636 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -25,7 +25,7 @@ Beiträge Feedback Webseite - Android-Implementierung des Passwortmanagers KeePass + Android-Implementierung des Passwortmanagers KeePass. Akzeptieren Eintrag hinzufügen Gruppe hinzufügen @@ -277,9 +277,7 @@ Bedienelemente hervorheben, um die Funktionsweise der App zu lernen Änderbar Schreibgeschützt - Schreibgeschützt Datenbank-Schreibschutz aktivieren - Datenbank standardmäßig schreibgeschützt öffnen Den Öffnungsmodus für die Sitzung ändern. \n \n„Schreibgeschützt“ verhindert unbeabsichtigte Änderungen an der Datenbank. \nMit „Änderbar“ lässt sich jedes Element frei bearbeiten, hinzufügen oder löschen. Eintrag bearbeiten Die Datenbank konnte nicht geladen werden. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 999e287d3..f585bfe61 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -259,8 +259,6 @@ Επισήμανση στοιχείων για να μάθετε πώς λειτουργεί η εφαρμογή Προστασία εγγραφής Τροποποιήσιμο - Προστασία Εγγραφής - Ανοίξτε τη βάση δεδομένων μόνο για ανάγνωση από προεπιλογή Προστασία Εγγραφής της βάσης δεδομένων σας Αλλάξτε τη λειτουργία ανοίγματος για το session. \n diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index e2fbb65f3..4c3e117dc 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -153,4 +153,4 @@ Por krei, malfermi kaj konservi datumbazaj dosieroj necesiĝas dosier-manipulilo, kiu akceptas intenco-agojn ACTION_CREATE_DOCUMENT kaj ACTION_OPEN_DOCUMENT. Periodo de malaktiveco antaŭ ŝlosado de datumbazo Etendita ASCII - \ No newline at end of file + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c6ce8639d..992ccb923 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -271,8 +271,6 @@ Permite pulsar el botón \"Abrir\" si no son seleccionadas las credenciales Consejos educativos Destaca los elementos para aprender cómo funciona la aplicación - Protegida contra escritura - Abre la base de datos como solo lectura por defecto Proteja la base de datos contra escritura Magikeyboard Magikeyboard (KeePassDX) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index de8e12b02..6340d30bb 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -1,7 +1,7 @@ Meie veebisait - KeePassi salasõnahalduri Androidi variant + KeePassi salasõnahalduri variant Androidile. Meie kontaktid Teave Nõustu @@ -84,7 +84,7 @@ Füüsilise võtme märkruut Mandaatide info Navigatsiooni päis - Laadime andmebaasi võtme… + Laadin andmebaasi võtit… Sõnu salafraasis Kasuta vaikeandmebaasina Viimati kasutatud @@ -128,7 +128,7 @@ Vali võtmefail. Salasõnad ei klapi. Andmebaasifaili loomine ei õnnestunud. - URL + Võrguaadress Faili loomine ei õnnestunud. Ühekordne salasõna Lõikelauale kopeeritud andmete hoidmise aeg (kui sinu seade sellist võimalust toetab) @@ -245,7 +245,7 @@ Loendite suurus Teksti suurus loendites Loome salasõnade andmebaasi… - Laadime salasõnade andmebaasi… + Laadin salasõnade andmebaasi… väiketähed Rakenduse teave Muuda peavõtit @@ -531,7 +531,6 @@ Automaattäite soovitused on lisatud. Kui andmebaas on avatud ainult lugemiseks, siis andmete salvestamine pole võimalik. Kustutab salasõna, mis oli kasutusel andmebaasiga ühenduse loomise ajal - Vaikimisi ava andmebaas vaid lugemiseks Luba teistel rakendusel teha sellest rakendusest ekraanitõmmist või salvestada tema ekraanivaadet Sulge andmebaas Peale automaattäite kasutamist sulega andmebaas @@ -547,7 +546,6 @@ Ära kasuta peavõtit Kui kasutajanimi või salasõna pole valitud, siis võimaldab klõpsida „Ava“ nuppu Kustuta salasõna - Kirjutuskaitstud Ekraanitõmmiste lubamine Koolitusvihjed Õppimaks, kuidas rakendus toimib, tõsta esile kasutajaliidese elemente diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index a7440d78e..2cad4c545 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -397,7 +397,6 @@ Gakoaren eratorpen funtzioak erabiliko duen memoria kopurua. Datuak gordetzeko eskatu formulario bat betetzean Ireki zure aurreko datu-base fitxategia zure fitxategi kudeatzailetik erabiltzen jarraitzeko. - Idazketaren aurka babestuta Aplikazioen blokeo zerrenda Tekla automatikoaren akzioa Aldatu automatikoki aurreko teklatura datu-basearen kredentzialen pantailan @@ -478,7 +477,6 @@ Zifraketa algoritmorako gakoa sortzeko, gako nagusia itxuraldatu egiten da eratorpen funtzio eta ausazko gatz baten bidez. Bilaketa pantaila Erabiltzaileari datu-baseko sarrera hautatzeko aukera erakutsi - Lehenetsi irakurketa soilerako datu-basea irekitzea Berrezarri hezkuntza-pistak Sarrerek zure identitate digitalak administratzen laguntzen dute. \n diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 76c466bef..a3d9ad08e 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -318,4 +318,4 @@ الگو ها اطلاعات رمز یکبار مصرف اطلاعات اعتبار نامه - \ No newline at end of file + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 1f9ac47f0..4d0aefe9c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -283,8 +283,6 @@ Autorise l’appui du bouton \"Ouvrir\" si aucun identifiant n’est sélectionné Protéger en écriture Modifiable - Protéger en écriture - Ouvre la base de données en lecture seule par défaut Protégez en écriture votre base de données Changez le mode d’ouverture pour la session. \n diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 13c39327f..9802a2569 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -556,7 +556,6 @@ Gardar base de datos automaticamente Manter a pantalla acesa Permitir pulsar o botón \"Abrir\" se non seleccionar ningunha credencial - Só lectura Destacar elementos para saber como funciona a aplicación Suxestións educativas restabelecidas Crear o teu arquivo de base de datos @@ -593,7 +592,6 @@ Engadir campos personalizados Completado de formularios %1$s dispoñíbel no Magikeyboard - Abrir a base de datos en modo de só lectura por defecto Configure a xestión do contrasinal dun só uso (HOTP / TOTP) para xerar un token solicitado para a autenticación de dous factores (2FA). Desbloquee a súa base de datos Eliminar contrasinal diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index d92e6dd67..c7f263b46 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -167,4 +167,4 @@ फ़ाइल डेटा को अपलोड करते समय एक त्रुटि हुई। फ़ाइल डेटा पहले से उपलब्ध है। जानकारी - \ No newline at end of file + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 7cb8ce296..c0f766924 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with KeePassDX. If not, see . --> - Android implementacija KeePass upravljača lozinki + Android implementacija KeePass upravljača lozinki. Prihvati Dodaj unos Uredi unos @@ -187,7 +187,7 @@ Ako automatsko brisanje međuspremnika ne uspije, izbriši njegovu povijest ručno. Zaključaj bazu podataka nakon par sekundi kad se ekran ugasi Pritisni \'Natrag\' za zaključavanje - Otključavanje uređaja + Otključaj uređaj Koristi otključavanje uređaja za jednostavnije otvaranje baze podataka Biometrijsko otključavanje Otvaranje baze podataka skeniranjem biometrike @@ -310,8 +310,6 @@ Dozvoljava dodir gumba „Otvori”, ako nisu odabrani nikoji podaci za prijavu Izbriši lozinku Briše upisanu lozinku nakon pokušaja povezivanja s bazom podataka - Zaštićeno od pisanja - Standardno otvori bazu podataka u zaštićenom stanju Automatski spremi bazu podataka Automatski spremi bazu podataka nakon svake važne radnje (samo u modusu „Promjenjivo”) Edukativne poruke @@ -385,7 +383,7 @@ Zaključaj bazu podataka brzo, aplikaciju možeš postaviti tako da bazu nakon nekog vremena zaključa i kad se ekran isključi. Prikaži mjesto nedavnih baza podataka Za brzo otključavanje baze podataka, poveži lozinku sa skeniranom biometrijom ili podacima za prijavu uređaja. - Kako bismo zadržali našu slobodu i uvijek bili aktivni, računamo na tvoj<strong>doprinos.</strong> + <strong>Doprinosom</strong> projektu <i>(novčano, kodom, prijevodom)</i> pomažeš mu da živi i napreduje, a također stječeš pravo na postupak otključavanja <strong>tema</strong>.</strong> Za stvaranje ključa za algoritam šifriranja, glavni ključ se transformira pomoću funkcije za generiranje ključeva koja sadrži slučajnu komponentu. Zaključaj bazu podataka kad korisnik pritisne gumb za natrag na glavnom ekranu Sakrij pokvarene poveznice baze podataka @@ -700,4 +698,7 @@ Rekurzivno izračunava broj unosa u grupi Sakrij predloške Predlošci se ne prikazuju + Tajni ključ mora sadržati barem %1$d znakova. + Generiraj datoteku ključeva + Ne preporučuje se dodavanje velike datoteke ključeva, jer to može spriječiti otvaranje baze podataka. diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 609d381f5..62457a73f 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -19,7 +19,7 @@ --> Visszajelzés Honlap - A KeePass jelszókezelő androidos megvalósítása + A KeePass jelszókezelő androidos megvalósítása. Elfogadás Bejegyzés hozzáadása Csoport hozzáadása @@ -245,8 +245,6 @@ Hang gombnyomáskor Mesterkulcs elhagyásának engedélyezése A „Megnyitás” gomb engedélyezése, ha nincsenek hitelesítő adatok kiválasztva - Írásvédett - Az adatbázis megnyitása alapértelmezetten írásvédett módban Oktatóképernyők Elemek kiemelése, hogy megtudja hogyan működik az alkalmazás Oktatóképernyők visszaállítása diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 7b0c15dc4..b0b64658b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -170,7 +170,7 @@ Rubah Entri Tambahkan Entri Terima - Implementasi Android dari pengelola kata sandi KeePass + Implementasi Android dari pengelola kata sandi KeePass. Beranda Umpan Balik Kontribusi @@ -515,7 +515,7 @@ Bantu meningkatkan stabilitas, keamanan, dan menambahkan lebih banyak fitur. Tidak seperti banyak aplikasi pengelola kata sandi, ini <strong>bebas iklan</strong>, <strong>perangkat lunak libre copyleft</strong>, dan tidak mengumpulkan data pribadi di peladennya, tidak peduli versi apa yang Anda gunakan. <strong>Gaya visual</strong> ini tersedia berkat kemurahan hati Anda. - Dengan <strong>berkontribusi<strong> kepada proyek <i>(dalam bentuk uang, kode, penerjemahan)<i>, Anda akan membantu proyek tersebut supaya terus hidup dan berkembang, dan Anda juga akan memenuhi syarat untuk prosedur pembukaan kunci <strong>tema<strong>. + Dengan <strong>berkontribusi</strong> kepada proyek <i>(dalam bentuk uang, kode, penerjemahan)</i>, Anda akan membantu proyek tersebut supaya terus hidup dan berkembang, dan Anda juga akan memenuhi syarat untuk prosedur pembukaan kunci <strong>tema</strong>. Fitur ini <strong>sedang dalam pengembangan</strong> dan memerlukan <strong>kontribusi</strong> Anda untuk bisa tersedia segera. Tampilkan Token OTP Teks @@ -554,7 +554,6 @@ Izinkan tidak ada kunci utama Memungkinkan mengetuk tombol \"Buka\" jika tidak ada kredensial yang dipilih Menghapus kata sandi yang dimasukkan setelah upaya koneksi ke basis data - Buka basis data baca-saja secara baku Biarkan layar nyala Sorot elemen untuk mempelajari cara kerja aplikasi Buat file pengelola kata sandi pertama Anda. @@ -618,7 +617,6 @@ \nPeriksa kompatibilitas dan keamanan KeyStore dengan produsen perangkat Anda dan pembuat ROM yang Anda gunakan. Lindungi basis data Anda dari penulisan Coba simpan informasi terbagi ketika membuat sebuah pilihan entri manual untuk penggunaan mudah di waktu mendatang - Terlindungi-tulis Jaga layar tetap menyala saat melihat atau menyunting sebuah entri Kotak centang kunci perangkat keras Menunggu untuk permintaan tantangan… @@ -685,4 +683,5 @@ Jumlah entri rekursif Tidak disarankan menambahkan berkas kunci yang besar, ini dapat mencegah pembukaan basis data. Sembunyikan templat - \ No newline at end of file + Kunci rahasia minimal harus %1$d karakter. + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 58ef0c4dd..f6ebcfda3 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -21,7 +21,7 @@ --> Commenti Pagina web - Implementazione Android del gestore password KeePass + Implementazione Android del gestore password KeePass. Accetto Aggiungi elemento Aggiungi gruppo @@ -222,8 +222,6 @@ Attiva una tastiera personale che inserisce le tue password e i campi di identità Non consentire chiavi principali Permetti di toccare il pulsante \"Apri\" se non sono selezionate credenziali - Protetto da scrittura - Apri il database in sola lettura in modo predefinito Suggerimenti educativi Evidenzia gli elementi per imparare come funziona l\'app Ripristina i suggerimenti educativi diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 54320e9cd..aef987119 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -334,7 +334,6 @@ אזהרה: לוח ההעתקה משותף לכל היישומים. אם מידע רגיש יועתק, תוכנות אחרות עשויות לשחזר אותו. השבת Magikeyboard (KeePassDX) - מוגן מפני כתיבה נקה בסגירה רשומה %1$s זמין ב־Magikeyboard @@ -469,7 +468,6 @@ פעולת מקש אוטומטית אפשר עבודה ללא מפתח ראשי מאפשר הקשה על הכפתור \"פתח\" אם לא נבחרו אישורים - פתח את מסד הנתונים לקריאה בלבד כברירת מחדל מחק סיסמה מוחק את הסיסמה שהוזנה לאחר ניסיון התחברות למסד נתונים הראה שוב את כל המידע הלימודי diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e7c2d0cf9..2c720c866 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -21,7 +21,7 @@ 貢献 フィードバック ホームページ - KeePass パスワード マネージャーの Android 実装 + KeePassパスワードマネージャーのAndroid実装版。 承認 エントリーを追加 エントリーを編集 @@ -400,8 +400,6 @@ 認証情報が選択されていない場合でも、[開く] ボタンのタップを許可します パスワードの削除 入力されたパスワードをデータベースへの接続試行後に削除します - 書き込み禁止 - デフォルトでデータベースを読み取り専用として開きます データベースの自動保存 重要なアクションを起こすたびにデータベースを保存します( [変更可能] モードのとき) 教育的なヒント @@ -445,7 +443,7 @@ 多くのパスワード管理アプリとは異なり、このアプリは<strong>広告なし</strong>かつ<strong>コピーレフトの自由ソフトウェア</strong>です。どのバージョンを使っても、サーバー上で個人情報が収集されることはありません。 Pro バージョンを購入すると、この<strong>ビジュアル スタイル</strong>にアクセスできるようになり、また<strong>コミュニティ プロジェクトの実現</strong>を特に支援できます。 この<strong>ビジュアル スタイル</strong>はあなたの厚意により利用可能となります。 - <strong>プロジェクトに貢献</strong>することによって(お金、コード、翻訳など)、プロジェクトは継続して成長することができ、さらに<strong>テーマ</strong>のアンロック手続きにも参加できるようになります。 + <strong>プロジェクトに貢献</strong>することによって <i>(お金、コード、翻訳など)</i>、プロジェクトは継続して成長することができ、さらに<strong>テーマ</strong>のアンロック手続きにも参加できるようになります。 この機能は<strong>開発中</strong>であり、早期に提供するにはあなたの<strong>貢献</strong>が必要です。 <strong>pro</strong> バージョンを購入することによる、 <strong>貢献</strong>による、 diff --git a/app/src/main/res/values-jv/strings.xml b/app/src/main/res/values-jv/strings.xml index fbd5c6ef0..2c3dbd7db 100644 --- a/app/src/main/res/values-jv/strings.xml +++ b/app/src/main/res/values-jv/strings.xml @@ -27,4 +27,4 @@ Ngarep Urun Rembuk Nomer Hape - \ No newline at end of file + diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 7ca277d4b..b880178ca 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -434,4 +434,4 @@ Įrenginio atrakinimo atpažinimas Rodyti užrakto mygtuką Turinys - \ No newline at end of file + diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 1a4cb99ac..2c5ca02f1 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -405,4 +405,4 @@ സിവിവി നമ്പർ ടെബിറ്റ്/ ക്രെഡിറ്റ് കാർഡ് - \ No newline at end of file + diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index 51cd01a6d..0611f4bcf 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -9,4 +9,4 @@ राणी चावी गट जोडा सुरक्षा - \ No newline at end of file + diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index 6fdd37b48..dd4233c6d 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -44,4 +44,4 @@ Password စစ်ဆေးရန် ဟက်ဝဲလ် ကီး စစ်ဆေးရန် ပစ္စည်း ရှေ့မျက်နှာစာ အရောင် - \ No newline at end of file + diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 4c72a6d63..ebdad3d16 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -19,7 +19,7 @@ --> Tilbakemelding Hjemmeside - Android-implementasjon av KeePass-passordsbehandleren + Android-implementasjon av KeePass-passordsbehandleren. Godta Legg til oppføring Rediger oppføring @@ -90,7 +90,7 @@ Lengde Passord Passord - Kunne ikke lese legitimasjonen. + Kunne ikke lese autentiseringsopplysningene. Ugyldig algoritme. Fremmed databaseformat. Nøkkelfilen er tom. @@ -168,7 +168,7 @@ Ønsker du å fortsette uten en krypteringsnøkkel\? Versjon %1$s Kryptert passord lagret - Denne databasen har ikke et passord enda. + Utilgjengelig Historikk Utseende Generelt @@ -200,8 +200,8 @@ Sti Tildel en hovednøkkel Opprett nytt hvelv - Papirkurv bruk - Flytter grupper og oppføringer til \"Papirkurv\"-gruppen før sletting + Papirkurvbruk + Flytter grupper og oppføringer til «Papirkurv»-gruppen før sletting Felt-skrift Endre skriften brukt i felter for bedre tegngjengivelse Kopi av passord @@ -218,8 +218,6 @@ Aktiver et egendefinert tastatur som fyller inn passordene og alle identitetsfelter Tillat ingen hovednøkkel Tillater å trykke på \"Åpne\"-knappen hvis ingen legitimasjon er valgt - Skrivebeskyttet - Åpne databasen skrivebeskyttet som standard Lærerike tips Fremhev elementer for å finne ut hvordan appen fungerer Tilbakestill lærerike tips @@ -246,14 +244,9 @@ \n \nHusk å lagre en kopi av din .kdbx-fil på et trygt sted etter hver endring. Skrivebeskytt databasen din - Endre åpningsmodus for økten. -\n -\nI skrivebeskyttet modus, kan du forhindre utilsiktede endringer i databasen. -\n -\nI skrivemodus, kan du legge til, slette eller endre alle elementene. + Endre åpningsmodus for sesjonen. \n \n«Skrivebeskyttet» hindrer utilsiktede endringer i databasen. \n«Modifiserbar» lar deg endre, slette eller modifisere alle elementene etter eget ønske. Kopier et felt - Kopier et felt for å lime det inn der du ønsker. -\nBruk den skjemautfyllingsmetoden du foretrekker. + Kopierte felt kan limes inn hvorsomhelst.\n\nBruk den utfyllingsmetoden du foretrekker. Lås databasen Lås din database raskt. Du kan sette opp programmet slik at det låser den etter en stund, og når skjermen slår seg av. Sorter elementer @@ -263,7 +256,7 @@ I motsetning til mange passordadministrasjons-apper er denne <strong>annonsefri</strong>, <strong>copylefted libre-programvare</strong> og samler ikke inn personlige data på serverne, uansett hvilken versjon du bruker. Ved kjøp av pro-versjonen, vil du få tilgang til denne <strong>visuelle funksjonen</strong> og du vil bidra med støtte til <strong> realisering av fellesskaps prosjekter.</strong> Denne <strong>visuelle stilen</strong> er tilgjengelig takket være din generøsitet. - For å beholde vår frihet og alltid være aktive, stoler vi på dine <strong>bidrag.</strong> + Ved å <strong>bidra</strong> til prosjektet <i>(med penger, kode eller oversettelser)</i>, bidrar du til å holde prosjektet gående og trivende, og du vil også kunne få mulighet til å låse opp <strong>drakter</strong>. Denne funksjonen er <strong>under utvikling</strong> og krever <strong>bidrag</strong> for å bli tilgjengelig snart. Ved å kjøpe <strong>pro</strong>-versjonen, Ved å <strong>bidra</strong>, @@ -334,7 +327,7 @@ Historikk Sett opp engangspassord OTP-type - Hemmelig + Hemmelighet Periode (sekunder) Teller Siffer @@ -345,7 +338,7 @@ Periode må være mellom %1$d og %2$d sekunder. Database åpnet Lagre data - Passord avkrysningsrute + Passordsavkryssningsboks Avkrysningsrute for nøkkelfil Biometrisk Minst én identitetsdetalj må angis. @@ -355,7 +348,7 @@ %1$s med samme UUID %2$s finnes allerede. Oppretter database… Sikkerhetsinnstillinger - Hovednøkkel innstillinger + Innstillinger for hovednøkkel Databasen inneholder dupliserte UUID-er. Datakomprimering Datakomprimering reduserer databasens størrelse @@ -411,7 +404,7 @@ Lagringsmodus Søkemodus Feltnavnet finnes allerede. - Legg til element + Legg til ny Opplåsing av enhetsdatabase B Viser UUID-en som er tilknyttet en oppføring eller en gruppe @@ -445,7 +438,7 @@ Gjenta for å skifte passordsynlighet Egendefinert Forvalg - Velg et lyst eller mørkt tema + Velg en lys eller mørk drakt Draktlysstyrke Rettetast Lagre delt info @@ -520,7 +513,7 @@ Sikkehetsnotis Standard SWIFT/BIC - Å lagre et nytt element tillates ikke i skrivebeskyttet database. + Å lagre et nytt element tillates ikke i en skrivebeskyttet database. Database-URI kan ikke innhentes. Kunne ikke gjenoppbygge listen på riktig måte. En feil inntraff under opplasting av fildataen. @@ -617,7 +610,7 @@ Utløpt Maskinvarenøkkel Velg en maskinvarenøkkel. - Kan ikke slå sammen fra en V1 database. + Kan ikke slå sammen med en kdb-databasefil. Databaseplasseringen er ukjent, databasehandlingen kan ikke utføres. Holder styr på maskinvarenøklene som brukes Ekskluder tvetydige tegn @@ -670,7 +663,7 @@ Spør Sammenslåingen er fullført Konfigurer - Temaer, farger, attributter + Drakter, farger, attributter Skjermbilde-modus Vurder karakterer Entropi: Beregn… @@ -685,7 +678,7 @@ Entropi: Høy små bokstaver STORE BOKSTAVER - Divine + Gudommelig Classic Enkel Moon @@ -695,4 +688,12 @@ Følg systemet Lyst Mørkt + Den hemmelige nøkkelen må være minst %1$d tegn. + Generer nøkkelfil + Noder + Rekursivt antall oppføringer + Regner ut antallet oppføringer i en gruppe rekursivt + Det anbefales ikke å legge til en stor nøkkelfil, siden dette kan hindre databasen i å åpnes. + Skjul maler + Maler vises ikke diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f80f31bca..aecbfa535 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -21,7 +21,7 @@ --> Feedback Startpagina - Android-implementatie van KeePass-wachtwoordmanager + Android-implementatie van de KeePass-wachtwoordmanager. Accepteren Item toevoegen Groep toevoegen @@ -230,8 +230,6 @@ Activeer een aangepast toetsenbord dat je wachtwoorden en identiteitsvelden vult Geen hoofdwachtwoord toestaan Schakel de knop \"Openen\" in als er geen referenties zijn geselecteerd - Alleen-lezen - Open de database standaard alleen-lezen Informatieve tips Markeer elementen om te leren hoe de app werkt Informatieve tips opnieuw instellen diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index e1ecba442..a819bff6c 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -331,4 +331,4 @@ Viser forgrunns- og bakgrunnsfargar for ei oppføring Vel ein ikonpakke Endre programmets ikonpakke - \ No newline at end of file + diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 7c36ba666..8a72d8efb 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -331,7 +331,6 @@ ਐਂਟਰੀ ਐਂਟਰੀ ਡਾਟਾਬੇਸ ਬੰਦ ਕਰੋ - ਲਿਖਣ ਤੋਂ ਸੁਰੱਖਿਅਤ ਐਂਟਰੀਆਂ ਵਿੱਚੋਂ ਲੱਭੋ ਐਂਟਰੀ ਨੂੰ ਸੋਧੋ ਅਟੈਚਮੈਂਟ ਜੋੜੋ @@ -346,4 +345,4 @@ <strong>ਹਿੱਸਾ ਪਾ ਕੇ</strong>, ਤੁਹਾਡੇ ਹਿੱਸਾ ਪਾਉਣ ਲਈ ਬਹੁਤ ਧੰਨਵਾਦ ਹੈ। UUID ਵੇਖਾਓ - \ No newline at end of file + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f1bea4812..cfb325993 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -19,7 +19,7 @@ --> Zgłoś błąd lub sugestię Strona główna - Implementacja menedżera haseł KeePass w systemie Android + Implementacja menedżera haseł KeePass w systemie Android. Akceptuj Dodaj wpis Dodaj grupę @@ -225,8 +225,6 @@ Aktywuj niestandardową klawiaturę wypełniającą hasła i wszystkie pola tożsamości Zezwalaj na brak klucza głównego Umożliwia naciśnięcie przycisku \"Otwórz\", jeśli nie wybrano żadnych poświadczeń - Ochrona przed zapisem - Domyślnie otwarte bazy danych są tylko do odczytu Wskazówki edukacyjne Podświetl elementy, aby dowiedzieć się, jak działa aplikacja Zresetuj wskazówki edukacyjne diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 45ee1be04..3b33e23ed 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -222,8 +222,6 @@ Ative um teclado customizado, populando suas senhas e todos os campos de identidade Permitir chave-mestra vazia Permite tocar no botão \"Abrir\" mesmo se nenhuma credencial for selecionada - Somente leitura - Abre o banco de dados no modo somente leitura por padrão Dicas educacionais Destaque os elementos para aprender como o aplicativo funciona Reiniciar telas educacionais @@ -707,4 +705,4 @@ Não é recomendável adicionar um arquivo-chave grande, pois isso pode impedir a abertura do banco de dados. Ocultar modelos Os modelos não são exibidos - \ No newline at end of file + diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index efc8ffab2..11ebb1a23 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -269,8 +269,6 @@ Permite tocar no botão \"Abrir\" se não estiverem selecionadas nenhumas credenciais Dicas educacionais Destacar elementos para saber como a aplicação funciona - Apenas leitura - Abrir a base de dados com permissão de apenas leitura por predefinição Proteger a base de dados contra alterações Altere o modo de abertura para a sessão. \n diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 276616a33..9da42fdc9 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1,7 +1,5 @@ - Abrir a base de dados com permissão de apenas leitura por predefinição - Apenas leitura Alterável Apenas leitura Destacar elementos para saber como a aplicação funciona @@ -685,4 +683,4 @@ Não é recomendado adicionar um arquivo-chave grande, pois isso pode impedir a abertura do banco de dados. Ocultar modelos Os modelos não são mostrados - \ No newline at end of file + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index a98a7546f..312e5f0fa 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -99,7 +99,7 @@ Nu s-a putut citi baza de date. Asigurați-vă că calea este corectă. Secret OTP nevalid. - Introduce un nume. + Introduceți un nume. Alege un fisier cheie. Nicio memorie pentru a încărca întreaga dvs. bază de date. Nu s-a putut încărca baza de date. @@ -107,7 +107,7 @@ Trebuie selectat cel puțin un tip de generare a parolei. Trebuie să setați cel puțin o credențială. Parolele nu se potrivesc. - \"Transformările rotunde\" prea sus. Setarea la 2147483648. + Prea multe runde de transformare. Setare la 2147483648. Fiecare șir trebuie să aibă un nume de câmp. Introduceți un număr întreg pozitiv în câmpul \"Lungime\". Nu s-a putut activa serviciul de completare automată. @@ -178,7 +178,7 @@ Mai puțin Niciodată Nu există rezultate de căutare - KeePassDX © %1$d Kunzisoft este <strong>open source</strong> și <strong>fără reclame</strong>. \n\nEste furnizat ca atare, sub licență <strong>GPLv3</strong>, fără nicio garanție. + KeePassDX © %1$d Kunzisoft este <strong>open source</strong> și <strong>fără reclame</strong>. \nEste furnizat ca atare, sub licență <strong>GPLv3</strong>, fără nicio garanție. Pentru a ne <strong>păstra libertatea</strong>, <strong>pentru a remedia erori</strong>, <strong>pentru a adăuga funcții</strong> și <strong>pentru a fi mereu activi</strong>, ne bazăm pe <strong>contribuția</strong> dvs. Ascundeți parolele Mascați parolele (***) în mod implicit @@ -336,8 +336,6 @@ Permite apăsarea butonului \"Deschidere\" în cazul în care nu sunt selectate credențiale Ștergere parolă Șterge parola introdusă după o încercare de conectare la o bază de date - Protejat la scriere - Deschideți baza de date numai în citire în mod implicit Salvare automată a bazei de date Salvați baza de date după fiecare acțiune importantă (în modul \"Modificabil\") Sugestii educaționale @@ -383,7 +381,7 @@ Spre deosebire de multe aplicații de gestionare a parolelor, aceasta este <strong>fără reclame</strong>, <strong>software liber cu copyleft</strong> și nu colectează date personale pe serverele sale, indiferent de versiunea pe care o utilizați. Prin cumpărarea versiunii pro, veți avea acces la acest lucru <strong>stil vizual</strong> și veți ajuta în special la <strong>realizarea proiectelor comunitare.</strong> Acest <strong> stil vizual </strong> este disponibil datorită generozității tale. - strong>Contribuind</strong> la proiect <i>(financiar, cod, traducere)</i>, îl veți ajuta să continue să trăiască și să prospere și veți fi, de asemenea, eligibil pentru procedura de deblocare a <strong>temei</strong>. + <strong>Contribuind</strong> la proiect <i>(financiar, cod, traducere)</i>, îl veți ajuta să continue să trăiască și să prospere și veți fi, de asemenea, eligibil pentru procedura de deblocare a <strong>temelor</strong>. Această caracteristică este <strong> în curs de dezvoltare</strong> și necesită ca <strong>contribuția</strong> dvs. să fie disponibilă în curând. Cumpărând versiunea <strong> pro </strong>, Prin <strong> contribuție </strong>, @@ -462,7 +460,7 @@ Date personalizate Etichete Tipul de OTP existent nu este recunoscut de acest formular, este posibil ca validarea acestuia să nu mai genereze corect token-ul. - Nu poți muta grupul aici. + Nu puteți muta un grup aici. Acest text nu se potrivește cu articolul solicitat. Salvarea unui element nou nu este permisă într-o bază de date numai pentru citire. Numele câmpului există deja. @@ -701,4 +699,5 @@ Nu este recomandat să adăugați un fișier cheie mare, acest lucru poate împiedica deschiderea bazei de date. Ascundeți șabloanele Șabloanele nu sunt afișate - \ No newline at end of file + Cheia secretă trebuie să conțină cel puțin %1$d caractere. + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0bdc680eb..d4884bc88 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -19,7 +19,7 @@ --> Отзыв Сайт - Android–версия менеджера паролей KeePass + Android–версия менеджера паролей KeePass. Принять Новая запись Новая группа @@ -226,8 +226,6 @@ Активируйте пользовательскую клавиатуру для простого заполнения паролей и любых идентификаторов Разрешить без главного пароля Разрешить нажимать кнопку \"Открыть\", если главный пароль не указан - Только чтение - По умолчанию открывать базу только для чтения Обучающие подсказки Выделять элементы, чтобы показать, как работает приложение Вернуть обучающие подсказки diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 054e31f8d..4decad49c 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -620,8 +620,6 @@ Umožňuje klepnúť na tlačidlo „Otvoriť“, ak nie sú vybraté žiadne poverenia Vymazať heslo Odstráni heslo zadané po pokuse o pripojenie k databáze - Ochrana proti zápisu - V predvolenom nastavení otvorte databázu len na čítanie Nechajte obrazovku zapnutú Pri sledovaní alebo úprave záznamu ponechať obrazovku zapnutú Režim snímky obrazovky diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index 3aa71146d..6b4c278fc 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -66,4 +66,4 @@ Zadnji dostop Kopiraj Doniraj - \ No newline at end of file + diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index bb39889c1..6d56c71c0 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -555,7 +555,6 @@ Shtypje tastesh me dridhje Ruaj hollësi kërkimi Listë bllokimesh që pengon vetëplotësim përkatësish web - Mbrojtur nga shkrimi Mbaje ekranin ndezur Ndihmëza edukative Jepni titull, emër përdoruesi, ose lëndë fushash të tjera, për të marrë fjalëkalimet tuaja. @@ -610,7 +609,6 @@ Shfaq mundësi për ta lënë përdoruesin të përzgjedhë zë baze të dhënash Provo të ruash informacion, kur bëhet një përzgjedhje dorazi e zërit, për përdorim më të kollajtë në të ardhmen S’lejohet ruajtje të dhënash për një bazë të dhënash të hapur vetëm-për-lexim. - Si parazgjedhje, bazën e të dhënave hape si vetëm-për-lexim Ruaje bazën e të dhënave pas çdo veprimi të rëndësishëm (nën mënyrën “E ndryshueshme”) Mbaje hapur ekranin, kur shihet ose përpunohet një zë Lejo aplikacione palësh të treta të regjistrojnë, ose bëjnë foto ekrani të aplikacionit diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index d5cb8ddf9..e9f87ee97 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -343,4 +343,4 @@ Чување базе података… Извршавање команде… Сортирај - \ No newline at end of file + diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 3342ad6b5..666254906 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -279,8 +279,6 @@ Markerar element för att lära dig hur appen fungerar Skrivskyddad Modifierbar - Skrivskyddad - Öppna databasen i skrivskyddat läge som standard Skrivskydda din databas Ändra öppningsläge för sessionen. \n diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index 970ebc979..946f50536 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -647,8 +647,6 @@ வலை களங்களை தானாக நிரப்புவதைத் தடுக்கும் பிளாக்லிச்ட் படிக்க மட்டும் திறக்கப்பட்ட தரவுத்தளத்திற்கு தரவு சேமிப்பு அனுமதிக்கப்படவில்லை. ஆட்டோஃபில் பரிந்துரைகள் சேர்க்கப்பட்டன. - எழுது பாதுகாக்கப்பட்ட - இயல்பாகவே தரவுத்தளத்தை படிக்க மட்டுமே திறக்கவும் உங்கள் டிசிட்டல் அடையாளங்களை நிர்வகிக்க உள்ளீடுகள் உதவுகின்றன.\n\n குழுக்கள் (~ கோப்புறைகள்) உங்கள் தரவுத்தளத்தில் உள்ளீடுகளை ஒழுங்கமைக்கின்றன. உள்ளீடுகள் மூலம் தேடுங்கள் உங்கள் நுழைவுடன் தொடர்புபடுத்த ஒரு வலுவான கடவுச்சொல்லை உருவாக்குங்கள், படிவத்தின் அளவுகோல்களின்படி அதை எளிதாக வரையறுக்கவும், பாதுகாப்பான கடவுச்சொல்லை மறந்துவிடாதீர்கள். diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 195344a31..9f2cdfbc8 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -416,7 +416,6 @@ การแนะนำแบบอินไลน์ ถามเพื่อบันทึกข้อมูล บล็อกการกรอกอัตโนมัติ - ป้องกันการเขียน รีเซ็ทคำแนะนำการใช้งาน แสดงคำแนะนำการใช้งานอีกครั้ง โดยการ<strong>ร่วมแก้ไข</strong> @@ -620,7 +619,6 @@ อนุญาตให้ไม่มีรหัสผ่านหลัก อนุญาตให้แตะปุ่ม \"เปิด\" เมื่อไม่มีข้อมูลประจำตัวถูกเลือก ลบรหัสผ่านที่ป้อนแล้วหลังจากพยายามเชื่อมต่อที่ฐานข้อมูล - เปิดฐานข้อมูลแบบอ่านอย่างเดียวเป็นค่าเรื่มต้น คำแนะนำการใช้งานถูกรีเซ็ทแล้ว ชุดไอคอน บันทึกฐานข้อมูลหลังจากการกระทำที่สำคัญ(ในโหมด\"แก้ไขได้\") diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index cafe7823b..28b5aa6b8 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -236,8 +236,6 @@ Tuşa basıldığında ses çıkar Ana anahtar olmamasına izin ver Seçili kimlik bilgisi yoksa \"Aç\" düğmesine dokunmaya izin verir - Yazma korumalı - Veri tabanını öntanımlı olarak salt okunur aç Eğitim ipuçları Uygulamanın nasıl çalıştığını öğrenmek için ögeleri vurgulayın Eğitici ipuçlarını sıfırla diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 5c08cfcb6..4b7fa159e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -19,7 +19,7 @@ --> Відгук Домівка - KeePassDX є Android-версією менеджера паролів KeePass + KeePassDX — Android-версія менеджера паролів KeePass. Прийняти Додати запис Додати групу @@ -314,8 +314,6 @@ Виділяти елементи, щоб дізнатися, як працює застосунок Навчальні підказки Автозбереження бази даних - Типово відкривати базу даних лише для читання - Захист від запису Видаляти пароль, введений після спроби з\'єднання з базою даних Видаляти пароль Дозволяє натискання «Відкрити», якщо не вибрано головний пароль diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index b2c1205a7..19f93744c 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -568,8 +568,6 @@ Cho phép nhấn vào nút \"Mở\" nếu không có thông tin xác thực nào được chọn Xóa mật khẩu Xóa mật khẩu đã nhập sau khi cố gắng kết nối với cơ sở dữ liệu - Bảo vệ chống ghi - Mở cơ sở dữ liệu ở chế độ chỉ đọc theo mặc định Tự động lưu cơ sở dữ liệu Lưu cơ sở dữ liệu sau mỗi hành động quan trọng (ở chế độ \"Có thể sửa đổi\") Giữ màn hình luôn bật diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index a53d4b5e0..46060749c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -19,7 +19,7 @@ --> 反馈 主页 - Android 平台上基于 KeePass 实现的密码管理器 + Android 平台 KeePass 密码管理器实现。 接受 添加条目 添加分组 @@ -190,8 +190,6 @@ 其他 键盘 魔法键盘 - 写入保护(只读模式) - 默认以只读方式打开数据库 下载 贡献 应用中使用的主题 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 51dfa818b..138325f66 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -218,8 +218,6 @@ 自動儲存資料庫 高亮界面元素來學習本應用工作方式 教學提示 - 預設以唯讀方式開啟資料庫 - 寫入保護(唯讀模式) 已儲存加密密碼 加密 加密演算法 @@ -280,7 +278,7 @@ 必須至少選擇一個密碼生成類型。 密碼不正確。 無法正確地重建列表。 - 唯讀資料庫不允許儲存新項目 + 唯讀資料庫不允許儲存新項目。 刪除檔案資料時發生了一個錯誤。 次數太多,最大設置到 2147483648。 無法儲存資料庫。 @@ -699,4 +697,5 @@ 節點 遞歸計算群組中的項目數 不建議增加較大的密鑰檔案,這可能會阻止資料庫開啟。 + 密鑰必須包含至少%1$d個字元。 diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 394e1dee0..f563e16ad 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -67,8 +67,6 @@ false delete_entered_password_key true - enable_read_only_key - false enable_auto_save_database_key true enable_keep_screen_on_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8fe153bda..d5dd2a8ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,7 +22,7 @@ Contribution Feedback Homepage - Android implementation of the KeePass password manager + Android implementation of the KeePass password manager. Accept Add entry Edit entry @@ -575,8 +575,6 @@ Allows tapping the \"Open\" button if no credentials are selected Delete password Deletes the password entered after a connection attempt to a database - Write-protected - Open the database read-only by default Autosave database Save the database after every important action (in \"Modifiable\" mode) Keep screen on @@ -738,4 +736,4 @@ Expired entries are not shown Hide templates Templates are not shown - \ No newline at end of file + diff --git a/app/src/main/res/xml/dataset_service.xml b/app/src/main/res/xml/dataset_service.xml index 870cf884a..94b60987b 100644 --- a/app/src/main/res/xml/dataset_service.xml +++ b/app/src/main/res/xml/dataset_service.xml @@ -28,269 +28,275 @@ Settings Activity. This is pointed to in the service's meta-data in the applicat tools:ignore="UnusedAttribute"> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="711900039" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="427912623" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> - - + android:maxLongVersionCode="711900039" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + + android:maxLongVersionCode="10000000000" /> + + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="2015836711" /> + android:maxLongVersionCode="2015849447" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> + android:maxLongVersionCode="10000000000" /> - + android:maxLongVersionCode="10000000000" /> diff --git a/app/src/main/res/xml/preferences_application.xml b/app/src/main/res/xml/preferences_application.xml index 8ee384ec5..41b2930b5 100644 --- a/app/src/main/res/xml/preferences_application.xml +++ b/app/src/main/res/xml/preferences_application.xml @@ -32,11 +32,6 @@ android:title="@string/delete_entered_password_title" android:summary="@string/delete_entered_password_summary" android:defaultValue="@bool/delete_entered_password_default"/> - database in a KDBX database, + * Merge a KDBX database in a KDBX database, * Try to take into account the modification date of each element * To make a merge as accurate as possible */ @@ -302,32 +304,113 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) { } // Manage deleted objects - databaseToMerge.deletedObjects.forEach { deletedObject -> - val deletedObjectId = deletedObject.uuid - val databaseEntry = database.getEntryById(deletedObjectId) - val databaseGroup = database.getGroupById(deletedObjectId) - val databaseIcon = database.iconsManager.getIcon(deletedObjectId) - val databaseIconModificationTime = databaseIcon?.lastModificationTime - if (databaseEntry != null - && deletedObject.deletionTime.isAfter(databaseEntry.lastModificationTime)) { - database.removeEntryFrom(databaseEntry, databaseEntry.parent) - } - if (databaseGroup != null - && deletedObject.deletionTime.isAfter(databaseGroup.lastModificationTime)) { - database.removeGroupFrom(databaseGroup, databaseGroup.parent) - } - if (databaseIcon != null - && ( - databaseIconModificationTime == null - || (deletedObject.deletionTime.isAfter(databaseIconModificationTime)) - ) - ) { - database.removeCustomIcon(deletedObjectId) - } + val deletedObjects = databaseToMerge.deletedObjects + deletedObjects.forEach { deletedObject -> + deleteEntry(deletedObject) + deleteGroup(deletedObject, deletedObjects) + deleteIcon(deletedObject) // Attachments are removed and optimized during the database save } } + /** + * Delete an entry from the database with the [deletedEntry] id + */ + private fun deleteEntry(deletedEntry: DeletedObject) { + val databaseEntry = database.getEntryById(deletedEntry.uuid) + if (databaseEntry != null + && deletedEntry.deletionTime.isAfter(databaseEntry.lastModificationTime)) { + database.removeEntryFrom(databaseEntry, databaseEntry.parent) + } + } + + /** + * Check whether a node is in the list of deleted objects + */ + private fun Set.containsNode(node: NodeVersioned): Boolean { + return this.any { it.uuid == node.nodeId.id } + } + + /** + * Check whether a node is not in the list of deleted objects + */ + private fun Set.notContainsNode(node: NodeVersioned): Boolean { + return !this.containsNode(node) + } + + /** + * Get the first parent not deleted + */ + private fun firstNotDeletedParent( + node: NodeVersioned, + deletedObjects: Set + ): GroupKDBX? { + var parent = node.parent + while (parent != null && deletedObjects.containsNode(parent)) { + parent = node.parent + } + return parent + } + + /** + * Delete a group from the database with the [deletedGroup] id + * Recursively check whether a group to be deleted contains a node not to be deleted with [deletedObjects] + * and move it to the first parent that has not been deleted. + */ + private fun deleteGroup(deletedGroup: DeletedObject, deletedObjects: Set) { + val databaseGroup = database.getGroupById(deletedGroup.uuid) + if (databaseGroup != null + && deletedGroup.deletionTime.isAfter(databaseGroup.lastModificationTime)) { + // Must be in dedicated list to prevent modification collision + val entriesToMove = mutableListOf() + databaseGroup.getChildEntries().forEach { child -> + // If the child entry is not a deleted object, + if (deletedObjects.notContainsNode(child)) { + entriesToMove.add(child) + } + } + val groupsToMove = mutableListOf() + databaseGroup.getChildGroups().forEach { child -> + // Move the group to the first parent not deleted + // the deleted objects will take care of remove it later + groupsToMove.add(child) + } + // For each node to move, move it + // try to move the child entry in the first parent not deleted + entriesToMove.forEach { child -> + database.removeEntryFrom(child, child.parent) + database.addEntryTo( + child, + firstNotDeletedParent(databaseGroup, deletedObjects) + ) + } + groupsToMove.forEach { child -> + database.removeGroupFrom(child, child.parent) + database.addGroupTo( + child, + firstNotDeletedParent(databaseGroup, deletedObjects) + ) + } + // Then delete the group + database.removeGroupFrom(databaseGroup, databaseGroup.parent) + } + } + + /** + * Delete an icon from the database with the [deletedIcon] id + */ + private fun deleteIcon(deletedIcon: DeletedObject) { + val deletedObjectId = deletedIcon.uuid + val databaseIcon = database.iconsManager.getIcon(deletedObjectId) + val databaseIconModificationTime = databaseIcon?.lastModificationTime + if (databaseIcon != null + && (databaseIconModificationTime == null + || (deletedIcon.deletionTime.isAfter(databaseIconModificationTime))) + ) { + database.removeCustomIcon(deletedObjectId) + } + } + /** * Merge [customDataToMerge] in [customData] */ diff --git a/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt b/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt index f8f3da992..3be886cd1 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt @@ -151,7 +151,7 @@ class SearchHelper { if (searchParameters.searchByDomain) { try { stringToCheck.inTheSameDomainAs(word, sameSubDomain = true) - } catch (e: Exception) { + } catch (_: Exception) { false } } else null @@ -204,10 +204,18 @@ class SearchHelper { regex.matches(stringToCheck) } else { specialComparison?.invoke(stringToCheck, searchParameters.searchQuery) - ?: stringToCheck.contains( - searchParameters.searchQuery, - !searchParameters.caseSensitive - ) + ?: run { + // Search with space separator #175 + var searchFound = true + searchParameters.searchQuery.split(" ").forEach { word -> + searchFound = searchFound + && stringToCheck.contains( + word, + !searchParameters.caseSensitive + ) + } + searchFound + } } } } diff --git a/database/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt b/database/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt index 92470c2fe..fa2320fe9 100644 --- a/database/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt +++ b/database/src/main/java/com/kunzisoft/keepass/model/DatabaseFile.kt @@ -6,6 +6,7 @@ import com.kunzisoft.keepass.hardware.HardwareKey data class DatabaseFile(var databaseUri: Uri? = null, var keyFileUri: Uri? = null, var hardwareKey: HardwareKey? = null, + var readOnly: Boolean? = null, var databaseDecodedPath: String? = null, var databaseAlias: String? = null, var databaseFileExists: Boolean = false, diff --git a/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt b/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt index 15b172db3..fa49bf6ab 100644 --- a/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt +++ b/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt @@ -92,7 +92,7 @@ inline fun Parcel.readParcelableCompat(): T? = when { fun Parcel.readParcelableCompat(clazz: Class): T? = when { SDK_INT >= 33 -> readParcelable(clazz.classLoader, clazz) - else -> @Suppress("DEPRECATION") readParcelable(clazz.classLoader) as? T + else -> @Suppress("DEPRECATION", "UNCHECKED_CAST") (readParcelable(clazz.classLoader) as? T) } inline fun Parcel.readSerializableCompat(): T? = when { @@ -120,19 +120,19 @@ fun Parcel.writeParcelableMap(map: Map, f inline fun Parcel.readParcelableMap(): Map { val size = readInt() val map = HashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: K? = try { when { SDK_INT >= 33 -> readParcelable(K::class.java.classLoader, K::class.java) else -> @Suppress("DEPRECATION") readParcelable(K::class.java.classLoader) } - } catch (e: Exception) { null } + } catch (_: Exception) { null } val value: V? = try { when { SDK_INT >= 33 -> readParcelable(V::class.java.classLoader, V::class.java) else -> @Suppress("DEPRECATION") readParcelable(V::class.java.classLoader) } - } catch (e: Exception) { null } + } catch (_: Exception) { null } if (key != null && value != null) map[key] = value } @@ -152,14 +152,14 @@ fun Parcel.writeStringParcelableMap(map: HashMap, fl inline fun Parcel.readStringParcelableMap(): LinkedHashMap { val size = readInt() val map = LinkedHashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: String? = readString() val value: V? = try { when { SDK_INT >= 33 -> readParcelable(V::class.java.classLoader, V::class.java) else -> @Suppress("DEPRECATION") readParcelable(V::class.java.classLoader) } - } catch (e: Exception) { null } + } catch (_: Exception) { null } if (key != null && value != null) map[key] = value } @@ -179,7 +179,7 @@ fun Parcel.writeStringIntMap(map: LinkedHashMap) { fun Parcel.readStringIntMap(): LinkedHashMap { val size = readInt() val map = LinkedHashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: String? = readString() val value: Int = readInt() if (key != null) @@ -201,7 +201,7 @@ fun Parcel.writeStringStringMap(map: MutableMap) { fun Parcel.readStringStringMap(): LinkedHashMap { val size = readInt() val map = LinkedHashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: String? = readString() val value: String? = readString() if (key != null && value != null) diff --git a/database/src/main/java/com/kunzisoft/keepass/utils/UriHelper.kt b/database/src/main/java/com/kunzisoft/keepass/utils/UriHelper.kt index 11e9389bb..28868038f 100644 --- a/database/src/main/java/com/kunzisoft/keepass/utils/UriHelper.kt +++ b/database/src/main/java/com/kunzisoft/keepass/utils/UriHelper.kt @@ -148,23 +148,6 @@ fun PackageManager.getPackageInfoCompat(packageName: String, flags: Int = 0): Pa @Suppress("DEPRECATION") getPackageInfo(packageName, flags) } -@SuppressLint("InlinedApi") -fun PackageManager.allowCreateDocumentByStorageAccessFramework(): Boolean { - return when { - // To check if a custom file manager can manage the ACTION_CREATE_DOCUMENT - // queries filter is in Manifest - Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> { - queryIntentActivitiesCompat( - Intent(Intent.ACTION_CREATE_DOCUMENT).apply { - addCategory(Intent.CATEGORY_OPENABLE) - type = "application/octet-stream" - }, PackageManager.MATCH_DEFAULT_ONLY - ).isNotEmpty() - } - else -> true - } -} - @SuppressLint("QueryPermissionsNeeded") private fun PackageManager.queryIntentActivitiesCompat(intent: Intent, flags: Int): List { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { diff --git a/fastlane/metadata/android/en-US/changelogs/141.txt b/fastlane/metadata/android/en-US/changelogs/141.txt new file mode 100644 index 000000000..7316a4d1f --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/141.txt @@ -0,0 +1,6 @@ + * Updated to API 35 minimum SDK 19 #2073 #2138 #2067 #2133 #1687 (Thx @Dev-ClayP) + * Remember last read-only state #2099 #2100 (Thx @rmacklin) + * Fix merge deletion #1516 + * Fix space in search #175 + * Fix deletable recycle bin #2163 + * Small fixes \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/141.txt b/fastlane/metadata/android/fr-FR/changelogs/141.txt new file mode 100644 index 000000000..c89f4045d --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/141.txt @@ -0,0 +1,6 @@ + * Mise à jour vers API 35 minimum SDK 19 #2073 #2138 #2067 #2133 #1687 (Thx @Dev-ClayP) + * Sauvegarde du dernier état lecture seule #2099 #2100 (Thx @rmacklin) + * Correction de la suppression lors d'un merge #1516 + * Correction des espaces dans la recherche #175 + * Correction de la poubelle supprimable #2163 + * Petites corrections \ No newline at end of file diff --git a/fastlane/metadata/android/hr/full_description.txt b/fastlane/metadata/android/hr/full_description.txt new file mode 100644 index 000000000..6fc26bd2d --- /dev/null +++ b/fastlane/metadata/android/hr/full_description.txt @@ -0,0 +1,23 @@ +KeePassDX je trezor i upravljač za lozinke koji omogućuje uređivanje šifriranih podataka u jednoj datoteci u otvorenom KeePass formatu i sigurno automatsko ispunjavanje obrazaca, ne zahtijeva internetsku vezu i pridržava se Android dizajnerskih smjernica. Aplikacija je otvorenog koda i bez oglasa. + +Funkcije + +- Stvaranje baza podataka, unosa i grupa. +- Podrška za .kdb i .kdbx datoteke (verzije 1 do 4) uz algoritme AES – Twofish – ChaCha20 – Argon2. +- Kompatibilnost s većinom alternativnih programa (KeePass, KeePassXC, KeeWeb, …). +- Brzo otvaranje i kopiranje URI / URL polja. +- Biometrijsko prepoznavanje za brzo otključavanje (otisak prsta / prepoznavanje lica / …). +- Upravljanje jednokratnim lozinkama (HOTP / TOTP) za dvofaktorsku autentifikaciju (2FA). +- Material dizajn s temama. +- Automatsko ispunjavanje i integracija. +- Tipkovnica za ispunjavanje polja. +- Dinamički predlošci. +- Povijest svakog unosa. +- Precizno upravljanje postavkama. +- Kod napisan u izvornim jezicima (Kotlin / Java / JNI / C). + +Doniraj ili kupi Pro verziju za bolju uslugu i brži razvoj željenih funkcija: https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro + +Projekt se neprestano razvija. Slobodno provjeri stanje razvoja sljedećih verzija: https://github.com/Kunzisoft/KeePassDX/projects + +Prijavi probleme na: https://github.com/Kunzisoft/KeePassDX/issues \ No newline at end of file diff --git a/fastlane/metadata/android/hr/short_description.txt b/fastlane/metadata/android/hr/short_description.txt new file mode 100644 index 000000000..31adbdb8e --- /dev/null +++ b/fastlane/metadata/android/hr/short_description.txt @@ -0,0 +1 @@ +Siguran trezor i upravljač za lozinke otvorenog koda \ No newline at end of file diff --git a/fastlane/metadata/android/hr/title.txt b/fastlane/metadata/android/hr/title.txt new file mode 100644 index 000000000..56c33275e --- /dev/null +++ b/fastlane/metadata/android/hr/title.txt @@ -0,0 +1 @@ +KeePassDX – FOSS trezor za lozinke \ No newline at end of file diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt new file mode 100644 index 000000000..a61178a85 --- /dev/null +++ b/fastlane/metadata/android/id/full_description.txt @@ -0,0 +1,23 @@ +KeePassDX adalah pengaman dan pengelola kata sandi yang memungkinkan pengeditan data terenkripsi dalam satu berkas dalam format KeePass terbuka dan pengisian formulir dengan cara yang aman, tidak memerlukan koneksi internet, dan mengintegrasikan standar desain Android. Aplikasi ini sumber terbuka, tanpa iklan. + +Fitur + +- Membuat berkas/entri dan grup basis data. +- Mendukung berkas .kdb dan .kdbx (versi 1 hingga 4) dengan algoritma AES - Twofish - ChaCha20 - Argon2. +- Kompatibel dengan sebagian besar program alternatif (KeePass, KeePassXC, KeeWeb, dll.). +- Memungkinkan pembukaan dan penyalinan kolom URI/URL dengan cepat. +- Pengenalan biometrik untuk pembukaan kunci cepat (sidik jari/pembuka kunci wajah/ dll.). +- Manajemen kata sandi satu kali (HOTP/TOTP) untuk autentikasi dua faktor (2FA). +- Desain Material dengan tema. +- Isi-Otomatis dan integrasi. +- Papan ketik untuk mengisi kolom. +- Templat dinamis. +- Riwayat setiap entri. +- Manajemen pengaturan yang presisi. +- Kode ditulis dalam bahasa asli (Kotlin/Java/JNI/C). + +Anda dapat berdonasi atau membeli versi pro untuk layanan yang lebih baik dan pengembangan fitur yang Anda inginkan dengan cepat: https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro + +Proyek ini terus berkembang. Jangan ragu untuk memeriksa status pengembangan pembaruan berikutnya: https://github.com/Kunzisoft/KeePassDX/projects + +Kirimkan masalah ke: https://github.com/Kunzisoft/KeePassDX/issues diff --git a/fastlane/metadata/android/id/short_description.txt b/fastlane/metadata/android/id/short_description.txt new file mode 100644 index 000000000..523255e6a --- /dev/null +++ b/fastlane/metadata/android/id/short_description.txt @@ -0,0 +1 @@ +Pengaman dan pengelola kata sandi yang aman dan sumber terbuka diff --git a/fastlane/metadata/android/id/title.txt b/fastlane/metadata/android/id/title.txt new file mode 100644 index 000000000..00694681a --- /dev/null +++ b/fastlane/metadata/android/id/title.txt @@ -0,0 +1 @@ +KeePassDX – Pengaman Sandi FOSS diff --git a/fastlane/metadata/android/it-IT/full_description.txt b/fastlane/metadata/android/it-IT/full_description.txt new file mode 100644 index 000000000..8e9548f30 --- /dev/null +++ b/fastlane/metadata/android/it-IT/full_description.txt @@ -0,0 +1,23 @@ +KeePassDX è un gestore di password che permette di modificare dati crittografati in un unico file nel formato aperto KeePass e di compilare moduli in modo sicuro, non richiede alcuna connessione a internet e integra gli standard di progettazione Android. L'app è open source, senza pubblicità. + +Caratteristiche + +- Crea file / voci e gruppi di database. +- Supporto ai file .kdb e .kdbx (versione da 1 a 4) con algoritmi AES - Twofish - ChaCha20 - Argon2. +- Compatibile con la maggior parte dei programmi alternativi (KeePass, KeePassXC, KeeWeb, …). +- Consente di aprire e copiare i campi URI / URL rapidamente. +- Riconoscimento biometrico per lo sblocco rapido (impronta digitale / sblocco facciale / …). +- Gestione di password una tantum (HOTP / TOTP) per l'autenticazione a due fattori (2FA). +- Material design con temi. +- Autocompletamento e integrazione. +- Tastiera con riempimento campi. +- Modelli dinamici. +- Cronologia di ogni voce. +- Gestione precisa delle impostazioni. +- Codice scritto in lingue native (Kotlin / Java / JNI / C). + +È possibile donare o acquistare la versione pro per un servizio migliore e uno sviluppo rapido di funzionalità desiderate: https://play.google.com/store/apps/details?id=com.kunzisoft.keepass.pro + +Il progetto è in continua evoluzione. Non esitate a controllare lo stato di sviluppo dei prossimi aggiornamenti: https://github.com/Kunzisoft/KeePassDX/projects + +Inviare problemi a: https://github.com/Kunzisoft/KeePassDX/issues diff --git a/fastlane/metadata/android/it-IT/short_description.txt b/fastlane/metadata/android/it-IT/short_description.txt new file mode 100644 index 000000000..c91e2af44 --- /dev/null +++ b/fastlane/metadata/android/it-IT/short_description.txt @@ -0,0 +1 @@ +Gestore di password sicuro e open-source diff --git a/fastlane/metadata/android/it-IT/title.txt b/fastlane/metadata/android/it-IT/title.txt index e45428c18..775fe685d 100644 --- a/fastlane/metadata/android/it-IT/title.txt +++ b/fastlane/metadata/android/it-IT/title.txt @@ -1 +1 @@ -KeePassDX - FOSS Password Safe +KeePassDX - gestore password diff --git a/fastlane/metadata/android/ru-RU/full_description.txt b/fastlane/metadata/android/ru-RU/full_description.txt index 9b81b638b..9a57c7dd3 100644 --- a/fastlane/metadata/android/ru-RU/full_description.txt +++ b/fastlane/metadata/android/ru-RU/full_description.txt @@ -19,3 +19,5 @@ KeePassDX имеет открытый исходный код и бе Вы можете пожертвовать или купить профессиональную версию чтобы улучшить обслуживание и ускорить разработку нужных вам возможностей: KeePass Pro Проект постоянно развивается. Не стесняйтесь проверять статус разработки и следующих обновлений тут: github.com/Kunzisoft/KeePassDX/projects + +Сообщить о проблеме:https://github.com/Kunzisoft/KeePassDX/issues diff --git a/fastlane/metadata/android/ru-RU/short_description.txt b/fastlane/metadata/android/ru-RU/short_description.txt index cc6d11c4b..a11d6f821 100644 --- a/fastlane/metadata/android/ru-RU/short_description.txt +++ b/fastlane/metadata/android/ru-RU/short_description.txt @@ -1 +1 @@ -Безопасный и свободный менеджер паролей, совместимый с файлами KeePass \ No newline at end of file +Безопасный и свободный менеджер паролей, совместимый с файлами KeePass diff --git a/fastlane/metadata/android/ru-RU/title.txt b/fastlane/metadata/android/ru-RU/title.txt index 75991a802..92196bff3 100644 --- a/fastlane/metadata/android/ru-RU/title.txt +++ b/fastlane/metadata/android/ru-RU/title.txt @@ -1 +1 @@ -KeePassDX - Менеджер паролей \ No newline at end of file +KeePassDX - Менеджер паролей diff --git a/icon-pack/build.gradle b/icon-pack/build.gradle index f4ba905ab..b65f008f5 100644 --- a/icon-pack/build.gradle +++ b/icon-pack/build.gradle @@ -3,11 +3,11 @@ apply plugin: 'kotlin-android' android { namespace 'com.kunzisoft.keepass.icon' - compileSdkVersion 34 + compileSdkVersion 36 defaultConfig { minSdkVersion 14 - targetSdkVersion 34 + targetSdkVersion 35 } compileOptions { diff --git a/icon-pack/classic/build.gradle b/icon-pack/classic/build.gradle index c5262974d..8d8688255 100644 --- a/icon-pack/classic/build.gradle +++ b/icon-pack/classic/build.gradle @@ -2,11 +2,11 @@ apply plugin: 'com.android.library' android { namespace 'com.kunzisoft.keepass.icon.classic' - compileSdkVersion 34 + compileSdkVersion 36 defaultConfig { minSdkVersion 14 - targetSdkVersion 34 + targetSdkVersion 35 } resourcePrefix 'classic_' diff --git a/icon-pack/material/build.gradle b/icon-pack/material/build.gradle index b4dfe5d79..2fbeac2fe 100644 --- a/icon-pack/material/build.gradle +++ b/icon-pack/material/build.gradle @@ -2,11 +2,11 @@ apply plugin: 'com.android.library' android { namespace 'com.kunzisoft.keepass.icon.material' - compileSdkVersion 34 + compileSdkVersion 36 defaultConfig { minSdkVersion 14 - targetSdkVersion 34 + targetSdkVersion 35 } resourcePrefix 'material_'