diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yml similarity index 98% rename from .github/ISSUE_TEMPLATE/bug_report.yaml rename to .github/ISSUE_TEMPLATE/bug_report.yml index ecd65ef2a..d08d4ae39 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,6 @@ name: Bug Report description: Report a bug. -title: "" -labels: bug +labels: ["bug"] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yml similarity index 97% rename from .github/ISSUE_TEMPLATE/feature_request.yaml rename to .github/ISSUE_TEMPLATE/feature_request.yml index 027913c26..e5204a75c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,6 @@ name: Feature request description: Suggest an idea. -title: "" -labels: feature +labels: ["feature"] body: - type: markdown attributes: 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..105810a1d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/AboutActivity.kt @@ -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 d615c1da1..d7f149202 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -644,16 +644,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 f26a7723a..081ca8d77 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -71,7 +71,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 @@ -262,7 +261,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(), GroupActivity.launch( this@FileDatabaseSelectActivity, database, - PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity) + false ) } ACTION_DATABASE_LOAD_TASK -> { @@ -329,13 +328,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/MainCredentialActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt index 2f247316c..b40e6b33a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt @@ -50,6 +50,7 @@ import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity +import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.biometric.DeviceUnlockFragment import com.kunzisoft.keepass.biometric.DeviceUnlockManager import com.kunzisoft.keepass.biometric.deviceUnlockError @@ -67,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 @@ -74,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 @@ -146,7 +148,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) @@ -202,6 +204,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 @@ -701,6 +710,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) } 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 9da467164..db384f28b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/NodesAdapter.kt @@ -538,9 +538,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/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 b129b372c..8c4eb9e91 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 0350c24b0..ee07349b3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateEditView.kt @@ -125,13 +125,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 b56891c63..2aceb2b36 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TemplateView.kt @@ -68,9 +68,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 { _, _ -> @@ -184,9 +182,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 430da31ea..fb53a360a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/TextFieldView.kt @@ -62,13 +62,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 { @@ -87,13 +86,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) } @@ -127,9 +124,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 { @@ -137,14 +132,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) + } } } @@ -152,18 +147,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 bfa94c107..596fd984f 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) } } diff --git a/app/src/main/res/layout/fragment_set_otp.xml b/app/src/main/res/layout/fragment_set_otp.xml index 14da1e1b0..872073917 100644 --- a/app/src/main/res/layout/fragment_set_otp.xml +++ b/app/src/main/res/layout/fragment_set_otp.xml @@ -122,8 +122,7 @@ android:inputType="textPassword" android:importantForAccessibility="no" android:importantForAutofill="no" - android:hint="@string/otp_secret" - tools:targetApi="jelly_bean" /> + 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/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 1cb256a24..494875b23 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -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..6ee009306 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 diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 41cb7e4ad..2091a718a 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -352,7 +352,6 @@ Настройки на Magikeyboard Избор на записи Разрешаване без главна парола - Само за четене %1$s Записът %1$s е достъпен в Magikeyboard Запис @@ -402,7 +401,6 @@ Бутон за заключване Включете услугата за попълване на формуляри в други приложения Свойства - По подразбиране отваря хранилището само за четене Не забравяйте да потвърдите записа и да го запазите в хранилището. \n \nАко се задейства автоматичното заключване, а сте направили промени, рискувате загуба на данни. 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..bf764789e 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -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..b98393928 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -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-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..bc89cded7 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -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-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-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 7cb8ce296..5d537d245 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -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 diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 609d381f5..d51d2a978 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -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..e4f226d30 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -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… diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 58ef0c4dd..37ef2ad12 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -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..768ea7262 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -400,8 +400,6 @@ 認証情報が選択されていない場合でも、[開く] ボタンのタップを許可します パスワードの削除 入力されたパスワードをデータベースへの接続試行後に削除します - 書き込み禁止 - デフォルトでデータベースを読み取り専用として開きます データベースの自動保存 重要なアクションを起こすたびにデータベースを保存します( [変更可能] モードのとき) 教育的なヒント diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 4c72a6d63..24233be04 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -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 diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f80f31bca..b4e7cbb1f 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -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-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 7c36ba666..6a2079916 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -331,7 +331,6 @@ ਐਂਟਰੀ ਐਂਟਰੀ ਡਾਟਾਬੇਸ ਬੰਦ ਕਰੋ - ਲਿਖਣ ਤੋਂ ਸੁਰੱਖਿਅਤ ਐਂਟਰੀਆਂ ਵਿੱਚੋਂ ਲੱਭੋ ਐਂਟਰੀ ਨੂੰ ਸੋਧੋ ਅਟੈਚਮੈਂਟ ਜੋੜੋ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f1bea4812..0764caae3 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -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..92d234c07 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 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..8016ca296 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 diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index a98a7546f..623deb078 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -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 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0bdc680eb..23b40a62f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -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-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-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..7e2dca9e4 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -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..d6d8858b9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -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..3ea1a9310 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 @@ 自動儲存資料庫 高亮界面元素來學習本應用工作方式 教學提示 - 預設以唯讀方式開啟資料庫 - 寫入保護(唯讀模式) 已儲存加密密碼 加密 加密演算法 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 676d474d7..d52376bfc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -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 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"/> - { - 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) {