diff --git a/CHANGELOG b/CHANGELOG index d1526d50b..5d97ac392 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +KeePassDX(2.5beta31) + * Add write permission to keep compatibility with old file managers + * Fix autofill for apps + * Auto search for autofill + * New keyfile input + * Icon to hide keyfile input + * New lock button + * Setting to hide lock button in user interface + * Clickable links in notes + * Fix autofill for key-value pairs + KeePassDX(2.5beta30) * Fix Lock after screen off (wait 1.5 seconds) * Upgrade autofill algorithm diff --git a/app/build.gradle b/app/build.gradle index 4ed1e26eb..f5ae37673 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 14 targetSdkVersion 29 - versionCode = 30 - versionName = "2.5beta30" + versionCode = 31 + versionName = "2.5RC1" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" @@ -97,6 +97,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.biometric:biometric:1.0.1' + implementation "androidx.core:core-ktx:1.2.0" // To upgrade with style implementation 'com.google.android.material:material:1.0.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e682eb930..cf5fb30a9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,6 @@ - + - diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index 5ab07809f..885d86f32 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -52,7 +52,6 @@ import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_ENTRY_HISTORY import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RESTORE_ENTRY_HISTORY import com.kunzisoft.keepass.settings.PreferencesUtil -import com.kunzisoft.keepass.settings.SettingsAutofillActivity import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager import com.kunzisoft.keepass.timeout.ClipboardHelper import com.kunzisoft.keepass.timeout.TimeoutHelper @@ -73,6 +72,7 @@ class EntryActivity : LockingActivity() { private var historyView: View? = null private var entryContentsView: EntryContentsView? = null private var entryProgress: ProgressBar? = null + private var lockView: View? = null private var toolbar: Toolbar? = null private var mDatabase: Database? = null @@ -124,6 +124,11 @@ class EntryActivity : LockingActivity() { entryContentsView = findViewById(R.id.entry_contents) entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this)) entryProgress = findViewById(R.id.entry_progress) + lockView = findViewById(R.id.lock_button) + + lockView?.setOnClickListener { + lockAndExit() + } // Init the clipboard helper clipboardHelper = ClipboardHelper(this) @@ -148,6 +153,13 @@ class EntryActivity : LockingActivity() { override fun onResume() { super.onResume() + // Show the lock button + lockView?.visibility = if (PreferencesUtil.showLockDatabaseButton(this)) { + View.VISIBLE + } else { + View.GONE + } + // Get Entry from UUID try { val keyEntry: NodeId? = intent.getParcelableExtra(KEY_ENTRY) @@ -462,8 +474,7 @@ class EntryActivity : LockingActivity() { getString(R.string.entry_user_name))) }, { - // Launch autofill settings - startActivity(Intent(this@EntryActivity, SettingsAutofillActivity::class.java)) + performedNextEducation(entryActivityEducation, menu) }) if (!entryCopyEducationPerformed) { @@ -526,10 +537,6 @@ class EntryActivity : LockingActivity() { !mReadOnly && mAutoSaveEnable) } } - R.id.menu_lock -> { - lockAndExit() - return true - } R.id.menu_save_database -> { mProgressDialogThread?.startDatabaseSave(!mReadOnly) } 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 1ea62dcf0..2c8f5af22 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -81,6 +81,7 @@ class EntryEditActivity : LockingActivity(), private var entryEditContentsView: EntryEditContentsView? = null private var entryEditAddToolBar: ActionMenuView? = null private var saveView: View? = null + private var lockView: View? = null // Education private var entryEditActivityEducation: EntryEditActivityEducation? = null @@ -112,6 +113,12 @@ class EntryEditActivity : LockingActivity(), .show(supportFragmentManager, "DatePickerFragment") } } + + lockView = findViewById(R.id.lock_button) + lockView?.setOnClickListener { + lockAndExit() + } + // Focus view to reinitialize timeout resetAppTimeoutWhenViewFocusedOrChanged(entryEditContentsView) @@ -241,6 +248,16 @@ class EntryEditActivity : LockingActivity(), } } + override fun onResume() { + super.onResume() + + lockView?.visibility = if (PreferencesUtil.showLockDatabaseButton(this)) { + View.VISIBLE + } else { + View.GONE + } + } + private fun populateViewsWithEntry(newEntry: Entry) { // Don't start the field reference manager, we want to see the raw ref mDatabase?.stopManageEntry(newEntry) @@ -416,10 +433,6 @@ class EntryEditActivity : LockingActivity(), override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.menu_lock -> { - lockAndExit() - return true - } R.id.menu_save_database -> { mProgressDialogThread?.startDatabaseSave(!mReadOnly) } 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 4d7d0dbf5..6058de700 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -26,13 +26,11 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle -import android.os.Environment import android.os.Handler import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.View -import android.widget.TextView import androidx.annotation.RequiresApi import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout @@ -48,9 +46,11 @@ import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.autofill.AutofillHelper +import com.kunzisoft.keepass.autofill.AutofillHelper.KEY_SEARCH_INFO import com.kunzisoft.keepass.database.action.ProgressDialogThread import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation +import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_TASK import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.utils.* @@ -63,7 +63,7 @@ class FileDatabaseSelectActivity : StylishActivity(), // Views private var coordinatorLayout: CoordinatorLayout? = null - private var fileListContainer: View? = null + private var fileManagerExplanationButton: View? = null private var createButtonView: View? = null private var openDatabaseButtonView: View? = null @@ -85,12 +85,16 @@ class FileDatabaseSelectActivity : StylishActivity(), setContentView(R.layout.activity_file_selection) coordinatorLayout = findViewById(R.id.activity_file_selection_coordinator_layout) - fileListContainer = findViewById(R.id.container_file_list) val toolbar = findViewById(R.id.toolbar) toolbar.title = "" setSupportActionBar(toolbar) + fileManagerExplanationButton = findViewById(R.id.file_manager_explanation_button) + fileManagerExplanationButton?.setOnClickListener { + UriUtil.gotoUrl(this, R.string.file_manager_explanation_url) + } + // Create button createButtonView = findViewById(R.id.create_database_button) if (allowCreateDocumentByStorageAccessFramework(packageManager)) { @@ -105,8 +109,13 @@ class FileDatabaseSelectActivity : StylishActivity(), createButtonView?.setOnClickListener { createNewFile() } mOpenFileHelper = OpenFileHelper(this) - openDatabaseButtonView = findViewById(R.id.open_database_button) - openDatabaseButtonView?.setOnClickListener(mOpenFileHelper?.openFileOnClickViewListener) + openDatabaseButtonView = findViewById(R.id.open_keyfile_button) + openDatabaseButtonView?.apply { + mOpenFileHelper?.openFileOnClickViewListener?.let { + setOnClickListener(it) + setOnLongClickListener(it) + } + } // History list val fileDatabaseHistoryRecyclerView = findViewById(R.id.file_list) @@ -121,7 +130,6 @@ class FileDatabaseSelectActivity : StylishActivity(), databaseFileUri, UriUtil.parse(fileDatabaseHistoryEntityToOpen.keyFileUri)) } - updateFileListVisibility() } mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete -> // Remove from app database @@ -130,7 +138,6 @@ class FileDatabaseSelectActivity : StylishActivity(), fileHistoryDeleted?.let { databaseFileHistoryDeleted -> mAdapterDatabaseHistory?.deleteDatabaseFileHistory(databaseFileHistoryDeleted) mAdapterDatabaseHistory?.notifyDataSetChanged() - updateFileListVisibility() } } true @@ -212,7 +219,8 @@ class FileDatabaseSelectActivity : StylishActivity(), try { PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, databaseUri, keyFile, - assistStructure) + assistStructure, + intent.getParcelableExtra(KEY_SEARCH_INFO)) } catch (e: FileNotFoundException) { fileNoFoundAction(e) } @@ -224,16 +232,21 @@ class FileDatabaseSelectActivity : StylishActivity(), private fun launchGroupActivity(readOnly: Boolean) { EntrySelectionHelper.doEntrySelectionAction(intent, { - GroupActivity.launch(this@FileDatabaseSelectActivity, readOnly) + GroupActivity.launch(this@FileDatabaseSelectActivity, + readOnly) }, { - GroupActivity.launchForKeyboardSelection(this@FileDatabaseSelectActivity, readOnly) + GroupActivity.launchForKeyboardSelection(this@FileDatabaseSelectActivity, + readOnly) // Do not keep history finish() }, { assistStructure -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - GroupActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, assistStructure, readOnly) + GroupActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, + assistStructure, + intent.getParcelableExtra(KEY_SEARCH_INFO), + readOnly) } }) } @@ -245,25 +258,6 @@ class FileDatabaseSelectActivity : StylishActivity(), overridePendingTransition(0, 0) } - private fun updateExternalStorageWarning() { - // To show errors - var warning = -1 - val state = Environment.getExternalStorageState() - if (state == Environment.MEDIA_MOUNTED_READ_ONLY) { - warning = R.string.read_only_warning - } else if (state != Environment.MEDIA_MOUNTED) { - warning = R.string.warning_unmounted - } - - val labelWarningView = findViewById(R.id.label_warning) - if (warning != -1) { - labelWarningView.setText(warning) - labelWarningView.visibility = View.VISIBLE - } else { - labelWarningView.visibility = View.INVISIBLE - } - } - override fun onResume() { val database = Database.getInstance() if (database.loaded) { @@ -272,8 +266,6 @@ class FileDatabaseSelectActivity : StylishActivity(), super.onResume() - updateExternalStorageWarning() - // Construct adapter with listeners if (PreferencesUtil.showRecentFiles(this)) { mFileDatabaseHistoryAction?.getAllFileDatabaseHistories { databaseFileHistoryList -> @@ -289,13 +281,11 @@ class FileDatabaseSelectActivity : StylishActivity(), true }) mAdapterDatabaseHistory?.notifyDataSetChanged() - updateFileListVisibility() } } } else { mAdapterDatabaseHistory?.clearDatabaseFileHistoryList() mAdapterDatabaseHistory?.notifyDataSetChanged() - updateFileListVisibility() } // Register progress task @@ -317,13 +307,6 @@ class FileDatabaseSelectActivity : StylishActivity(), outState.putParcelable(EXTRA_DATABASE_URI, mDatabaseFileUri) } - private fun updateFileListVisibility() { - if (mAdapterDatabaseHistory?.itemCount == 0) - fileListContainer?.visibility = View.INVISIBLE - else - fileListContainer?.visibility = View.VISIBLE - } - override fun onAssignKeyDialogPositiveClick( masterPasswordChecked: Boolean, masterPassword: String?, keyFileChecked: Boolean, keyFile: Uri?) { @@ -454,10 +437,13 @@ class FileDatabaseSelectActivity : StylishActivity(), */ @RequiresApi(api = Build.VERSION_CODES.O) - fun launchForAutofillResult(activity: Activity, assistStructure: AssistStructure) { + fun launchForAutofillResult(activity: Activity, + assistStructure: AssistStructure, + searchInfo: SearchInfo?) { AutofillHelper.startActivityForAutofillResult(activity, Intent(activity, FileDatabaseSelectActivity::class.java), - assistStructure) + assistStructure, + searchInfo) } } } 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 28a2a397d..8ce18e0a3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.kt @@ -62,6 +62,7 @@ import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.education.GroupActivityEducation import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.magikeyboard.MagikIME +import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_COPY_NODES_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_GROUP_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_DELETE_NODES_TASK @@ -89,6 +90,7 @@ class GroupActivity : LockingActivity(), // Views private var coordinatorLayout: CoordinatorLayout? = null + private var lockView: View? = null private var toolbar: Toolbar? = null private var searchTitleView: View? = null private var toolbarAction: ToolbarAction? = null @@ -134,6 +136,11 @@ class GroupActivity : LockingActivity(), groupNameView = findViewById(R.id.group_name) toolbarAction = findViewById(R.id.toolbar_action) modeTitleView = findViewById(R.id.mode_title_view) + lockView = findViewById(R.id.lock_button) + + lockView?.setOnClickListener { + lockAndExit() + } toolbar?.title = "" setSupportActionBar(toolbar) @@ -347,7 +354,7 @@ class GroupActivity : LockingActivity(), // If it's a search if (Intent.ACTION_SEARCH == intent.action) { val searchString = intent.getStringExtra(SearchManager.QUERY)?.trim { it <= ' ' } ?: "" - return mDatabase?.search(searchString) + return mDatabase?.createVirtualGroupFromSearch(searchString) } // else a real group else { @@ -486,7 +493,7 @@ class GroupActivity : LockingActivity(), // Build response with the entry selected if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && mDatabase != null) { mDatabase?.let { database -> - AutofillHelper.buildResponseWhenEntrySelected(this@GroupActivity, + AutofillHelper.buildResponse(this@GroupActivity, entryVersioned.getEntryInfo(database)) } } @@ -632,6 +639,13 @@ class GroupActivity : LockingActivity(), override fun onResume() { super.onResume() + + // Show the lock button + lockView?.visibility = if (PreferencesUtil.showLockDatabaseButton(this)) { + View.VISIBLE + } else { + View.GONE + } // Refresh the elements assignGroupViewElements() // Refresh suggestions to change preferences @@ -753,12 +767,11 @@ class GroupActivity : LockingActivity(), if (!sortMenuEducationPerformed) { // lockMenuEducationPerformed - toolbar != null - && toolbar!!.findViewById(R.id.menu_lock) != null - && groupActivityEducation.checkAndPerformedLockMenuEducation( - toolbar!!.findViewById(R.id.menu_lock), + val lockButtonView = findViewById(R.id.lock_button_icon) + lockButtonView != null + && groupActivityEducation.checkAndPerformedLockMenuEducation(lockButtonView, { - onOptionsItemSelected(menu.findItem(R.id.menu_lock)) + lockAndExit() }, { performedNextEducation(groupActivityEducation, menu) @@ -777,10 +790,6 @@ class GroupActivity : LockingActivity(), R.id.menu_search -> //onSearchRequested(); return true - R.id.menu_lock -> { - lockAndExit() - return true - } R.id.menu_save_database -> { mProgressDialogThread?.startDatabaseSave(!mReadOnly) return true @@ -956,27 +965,41 @@ class GroupActivity : LockingActivity(), private const val SEARCH_FRAGMENT_TAG = "SEARCH_FRAGMENT_TAG" private const val OLD_GROUP_TO_UPDATE_KEY = "OLD_GROUP_TO_UPDATE_KEY" - private fun buildIntent(context: Context, group: Group?, readOnly: Boolean, + private fun buildIntent(context: Context, + group: Group?, + searchInfo: SearchInfo?, + readOnly: Boolean, intentBuildLauncher: (Intent) -> Unit) { val intent = Intent(context, GroupActivity::class.java) if (group != null) { intent.putExtra(GROUP_ID_KEY, group.nodeId) } + if (searchInfo != null) { + intent.action = Intent.ACTION_SEARCH + val searchQuery = searchInfo.webDomain ?: searchInfo.applicationId + intent.putExtra(SearchManager.QUERY, searchQuery) + } ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly) intentBuildLauncher.invoke(intent) } - private fun checkTimeAndBuildIntent(activity: Activity, group: Group?, readOnly: Boolean, + private fun checkTimeAndBuildIntent(activity: Activity, + group: Group?, + searchInfo: SearchInfo?, + readOnly: Boolean, intentBuildLauncher: (Intent) -> Unit) { if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { - buildIntent(activity, group, readOnly, intentBuildLauncher) + buildIntent(activity, group, searchInfo, readOnly, intentBuildLauncher) } } - private fun checkTimeAndBuildIntent(context: Context, group: Group?, readOnly: Boolean, + private fun checkTimeAndBuildIntent(context: Context, + group: Group?, + searchInfo: SearchInfo?, + readOnly: Boolean, intentBuildLauncher: (Intent) -> Unit) { if (TimeoutHelper.checkTime(context)) { - buildIntent(context, group, readOnly, intentBuildLauncher) + buildIntent(context, group, searchInfo, readOnly, intentBuildLauncher) } } @@ -985,11 +1008,9 @@ class GroupActivity : LockingActivity(), * Standard Launch * ------------------------- */ - - @JvmOverloads fun launch(context: Context, readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(context)) { - checkTimeAndBuildIntent(context, null, readOnly) { intent -> + checkTimeAndBuildIntent(context, null, null, readOnly) { intent -> context.startActivity(intent) } } @@ -1000,10 +1021,9 @@ class GroupActivity : LockingActivity(), * ------------------------- */ // TODO implement pre search to directly open the direct group - fun launchForKeyboardSelection(context: Context, readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(context)) { - checkTimeAndBuildIntent(context, null, readOnly) { intent -> + checkTimeAndBuildIntent(context, null, null, readOnly) { intent -> EntrySelectionHelper.startActivityForEntrySelection(context, intent) } } @@ -1013,14 +1033,13 @@ class GroupActivity : LockingActivity(), * Autofill Launch * ------------------------- */ - // TODO implement pre search to directly open the direct group - @RequiresApi(api = Build.VERSION_CODES.O) fun launchForAutofillResult(activity: Activity, assistStructure: AssistStructure, + searchInfo: SearchInfo? = null, readOnly: Boolean = PreferencesUtil.enableReadOnlyDatabase(activity)) { - checkTimeAndBuildIntent(activity, null, readOnly) { intent -> - AutofillHelper.startActivityForAutofillResult(activity, intent, assistStructure) + checkTimeAndBuildIntent(activity, null, searchInfo, readOnly) { intent -> + AutofillHelper.startActivityForAutofillResult(activity, intent, assistStructure, searchInfo) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt index b935f43eb..a28fb059d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -23,6 +23,7 @@ import android.app.Activity import android.app.assist.AssistStructure import android.app.backup.BackupManager import android.content.Intent +import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle @@ -32,13 +33,11 @@ import android.text.TextWatcher import android.util.Log import android.view.* import android.view.inputmethod.EditorInfo.IME_ACTION_DONE -import android.widget.Button -import android.widget.CompoundButton -import android.widget.EditText -import android.widget.TextView +import android.widget.* import androidx.annotation.RequiresApi import androidx.appcompat.widget.Toolbar import androidx.biometric.BiometricManager +import androidx.core.app.ActivityCompat import com.google.android.material.snackbar.Snackbar import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog @@ -50,11 +49,13 @@ import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.autofill.AutofillHelper +import com.kunzisoft.keepass.autofill.AutofillHelper.KEY_SEARCH_INFO import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager import com.kunzisoft.keepass.database.action.ProgressDialogThread import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException import com.kunzisoft.keepass.education.PasswordActivityEducation +import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.CIPHER_ENTITY_KEY import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY @@ -66,6 +67,7 @@ import com.kunzisoft.keepass.utils.FileDatabaseInfo import com.kunzisoft.keepass.utils.MenuUtil import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.view.AdvancedUnlockInfoView +import com.kunzisoft.keepass.view.KeyFileSelectionView import com.kunzisoft.keepass.view.asError import kotlinx.android.synthetic.main.activity_password.* import java.io.FileNotFoundException @@ -77,7 +79,7 @@ open class PasswordActivity : StylishActivity() { private var containerView: View? = null private var filenameView: TextView? = null private var passwordView: EditText? = null - private var keyFileView: EditText? = null + private var keyFileSelectionView: KeyFileSelectionView? = null private var confirmButtonView: Button? = null private var checkboxPasswordView: CompoundButton? = null private var checkboxKeyFileView: CompoundButton? = null @@ -92,6 +94,7 @@ open class PasswordActivity : StylishActivity() { private var mRememberKeyFile: Boolean = false private var mOpenFileHelper: OpenFileHelper? = null + private var mPermissionAsked = false private var readOnly: Boolean = false private var mForceReadOnly: Boolean = false set(value) { @@ -123,18 +126,23 @@ open class PasswordActivity : StylishActivity() { confirmButtonView = findViewById(R.id.activity_password_open_button) filenameView = findViewById(R.id.filename) passwordView = findViewById(R.id.password) - keyFileView = findViewById(R.id.pass_keyfile) + keyFileSelectionView = findViewById(R.id.keyfile_selection) checkboxPasswordView = findViewById(R.id.password_checkbox) checkboxKeyFileView = findViewById(R.id.keyfile_checkox) checkboxDefaultDatabaseView = findViewById(R.id.default_database) advancedUnlockInfoView = findViewById(R.id.biometric_info) infoContainerView = findViewById(R.id.activity_password_info_container) + mPermissionAsked = savedInstanceState?.getBoolean(KEY_PERMISSION_ASKED) ?: mPermissionAsked readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState) - val browseView = findViewById(R.id.open_database_button) mOpenFileHelper = OpenFileHelper(this@PasswordActivity) - browseView.setOnClickListener(mOpenFileHelper!!.openFileOnClickViewListener) + keyFileSelectionView?.apply { + mOpenFileHelper?.openFileOnClickViewListener?.let { + setOnClickListener(it) + setOnLongClickListener(it) + } + } passwordView?.setOnEditorActionListener(onEditorActionListener) passwordView?.addTextChangedListener(object : TextWatcher { @@ -147,17 +155,6 @@ open class PasswordActivity : StylishActivity() { checkboxPasswordView?.isChecked = true } }) - keyFileView?.setOnEditorActionListener(onEditorActionListener) - keyFileView?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} - - override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} - - override fun afterTextChanged(editable: Editable) { - if (editable.toString().isNotEmpty() && checkboxKeyFileView?.isChecked != true) - checkboxKeyFileView?.isChecked = true - } - }) enableButtonOnCheckedChangeListener = CompoundButton.OnCheckedChangeListener { _, _ -> enableOrNotTheConfirmationButton() @@ -260,16 +257,38 @@ open class PasswordActivity : StylishActivity() { private fun launchGroupActivity() { EntrySelectionHelper.doEntrySelectionAction(intent, { - GroupActivity.launch(this@PasswordActivity, readOnly) + GroupActivity.launch(this@PasswordActivity, + readOnly) }, { - GroupActivity.launchForKeyboardSelection(this@PasswordActivity, readOnly) + GroupActivity.launchForKeyboardSelection(this@PasswordActivity, + readOnly) // Do not keep history finish() }, { assistStructure -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - GroupActivity.launchForAutofillResult(this@PasswordActivity, assistStructure, readOnly) + val searchInfo: SearchInfo? = intent.getParcelableExtra(KEY_SEARCH_INFO) + AutofillHelper.checkAutoSearchInfo(this, + Database.getInstance(), + searchInfo, + { items -> + // Response is build + AutofillHelper.buildResponse(this, items) + finish() + }, + { + // Here no search info found + GroupActivity.launchForAutofillResult(this@PasswordActivity, + assistStructure, + null, + readOnly) + }, + { + // Simply close if database not opened, normally not happened + finish() + } + ) } }) } @@ -285,11 +304,12 @@ open class PasswordActivity : StylishActivity() { } override fun onResume() { - mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this) if (Database.getInstance().loaded) launchGroupActivity() + mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this) + // If the database isn't accessible make sure to clear the password field, if it // was saved in the instance state if (Database.getInstance().loaded) { @@ -305,6 +325,7 @@ open class PasswordActivity : StylishActivity() { } override fun onSaveInstanceState(outState: Bundle) { + outState.putBoolean(KEY_PERMISSION_ASKED, mPermissionAsked) mDatabaseKeyFileUri?.let { outState.putString(KEY_KEYFILE, it.toString()) } @@ -319,7 +340,9 @@ open class PasswordActivity : StylishActivity() { !FileDatabaseInfo(this, it).canWrite } ?: false */ - mForceReadOnly = false + mForceReadOnly = mDatabaseFileUri?.let { + !FileDatabaseInfo(this, it).exists + } ?: true // Post init uri with KeyFile if needed if (mRememberKeyFile && (mDatabaseKeyFileUri == null || mDatabaseKeyFileUri.toString().isEmpty())) { @@ -345,7 +368,7 @@ open class PasswordActivity : StylishActivity() { // Define Key File text if (mRememberKeyFile) { - populateKeyFileTextView(keyFileUri?.toString()) + populateKeyFileTextView(keyFileUri) } // Define listeners for default database checkbox and validate button @@ -459,13 +482,13 @@ open class PasswordActivity : StylishActivity() { } } - private fun populateKeyFileTextView(text: String?) { - if (text == null || text.isEmpty()) { - keyFileView?.setText("") + private fun populateKeyFileTextView(uri: Uri?) { + if (uri == null || uri.toString().isEmpty()) { + keyFileSelectionView?.uri = null if (checkboxKeyFileView?.isChecked == true) checkboxKeyFileView?.isChecked = false } else { - keyFileView?.setText(text) + keyFileSelectionView?.uri = uri if (checkboxKeyFileView?.isChecked != true) checkboxKeyFileView?.isChecked = true } @@ -486,7 +509,7 @@ open class PasswordActivity : StylishActivity() { private fun verifyCheckboxesAndLoadDatabase(cipherDatabaseEntity: CipherDatabaseEntity? = null) { val password: String? = passwordView?.text?.toString() - val keyFile: Uri? = UriUtil.parse(keyFileView?.text?.toString()) + val keyFile: Uri? = keyFileSelectionView?.uri verifyCheckboxesAndLoadDatabase(password, keyFile, cipherDatabaseEntity) } @@ -499,7 +522,7 @@ open class PasswordActivity : StylishActivity() { } private fun verifyKeyFileCheckboxAndLoadDatabase(password: String?) { - val keyFile: Uri? = UriUtil.parse(keyFileView?.text?.toString()) + val keyFile: Uri? = keyFileSelectionView?.uri verifyKeyFileCheckbox(keyFile) loadDatabase(mDatabaseFileUri, password, mDatabaseKeyFileUri) } @@ -571,11 +594,42 @@ open class PasswordActivity : StylishActivity() { super.onCreateOptionsMenu(menu) - launchEducation(menu) + launchEducation(menu) { + launchCheckPermission() + } return true } + // Check permission + private fun launchCheckPermission() { + val writePermission = android.Manifest.permission.WRITE_EXTERNAL_STORAGE + val permissions = arrayOf(writePermission) + if (Build.VERSION.SDK_INT >= 23 + && !readOnly + && !mPermissionAsked) { + mPermissionAsked = true + // Check self permission to show or not the dialog + if (toolbar != null + && ActivityCompat.checkSelfPermission(this, writePermission) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, permissions, WRITE_EXTERNAL_STORAGE_REQUEST) + } + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + + when (requestCode) { + WRITE_EXTERNAL_STORAGE_REQUEST -> { + if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) + Toast.makeText(this, R.string.read_only_warning, Toast.LENGTH_LONG).show() + } + } + } + } + // To fix multiple view education private var performedEductionInProgress = false private fun launchEducation(menu: Menu, onEducationFinished: (()-> Unit)? = null) { @@ -678,7 +732,7 @@ open class PasswordActivity : StylishActivity() { ) { uri -> if (uri != null) { mDatabaseKeyFileUri = uri - populateKeyFileTextView(uri.toString()) + populateKeyFileTextView(uri) } } } @@ -703,6 +757,8 @@ open class PasswordActivity : StylishActivity() { private const val KEY_PASSWORD = "password" private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately" + private const val KEY_PERMISSION_ASKED = "KEY_PERMISSION_ASKED" + private const val WRITE_EXTERNAL_STORAGE_REQUEST = 647 private fun buildAndLaunchIntent(activity: Activity, databaseFile: Uri, keyFile: Uri?, intentBuildLauncher: (Intent) -> Unit) { @@ -757,13 +813,15 @@ open class PasswordActivity : StylishActivity() { activity: Activity, databaseFile: Uri, keyFile: Uri?, - assistStructure: AssistStructure?) { + assistStructure: AssistStructure?, + searchInfo: SearchInfo?) { if (assistStructure != null) { buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> AutofillHelper.startActivityForAutofillResult( activity, intent, - assistStructure) + assistStructure, + searchInfo) } } else { launch(activity, databaseFile, keyFile) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt index 1d2eb020d..64aa0d5d5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt @@ -35,7 +35,7 @@ import android.widget.CompoundButton import android.widget.TextView import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.helpers.OpenFileHelper -import com.kunzisoft.keepass.utils.UriUtil +import com.kunzisoft.keepass.view.KeyFileSelectionView class AssignMasterKeyDialogFragment : DialogFragment() { @@ -51,9 +51,8 @@ class AssignMasterKeyDialogFragment : DialogFragment() { private var passwordRepeatTextInputLayout: TextInputLayout? = null private var passwordRepeatView: TextView? = null - private var keyFileTextInputLayout: TextInputLayout? = null private var keyFileCheckBox: CompoundButton? = null - private var keyFileView: TextView? = null + private var keyFileSelectionView: KeyFileSelectionView? = null private var mListener: AssignPasswordDialogListener? = null @@ -69,16 +68,6 @@ class AssignMasterKeyDialogFragment : DialogFragment() { } } - private val keyFileTextWatcher = object : TextWatcher { - override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} - - override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} - - override fun afterTextChanged(editable: Editable) { - keyFileCheckBox?.isChecked = true - } - } - interface AssignPasswordDialogListener { fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?, keyFileChecked: Boolean, keyFile: Uri?) @@ -121,13 +110,14 @@ class AssignMasterKeyDialogFragment : DialogFragment() { passwordRepeatTextInputLayout = rootView?.findViewById(R.id.password_repeat_input_layout) passwordRepeatView = rootView?.findViewById(R.id.pass_conf_password) - keyFileTextInputLayout = rootView?.findViewById(R.id.keyfile_input_layout) keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox) - keyFileView = rootView?.findViewById(R.id.pass_keyfile) + keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection) mOpenFileHelper = OpenFileHelper(this) - rootView?.findViewById(R.id.open_database_button)?.setOnClickListener { view -> - mOpenFileHelper?.openFileOnClickViewListener?.onClick(view) } + keyFileSelectionView?.apply { + setOnClickListener(mOpenFileHelper?.openFileOnClickViewListener) + setOnLongClickListener(mOpenFileHelper?.openFileOnClickViewListener) + } val dialog = builder.create() @@ -176,14 +166,12 @@ class AssignMasterKeyDialogFragment : DialogFragment() { // To check checkboxes if a text is present passwordView?.addTextChangedListener(passwordTextWatcher) - keyFileView?.addTextChangedListener(keyFileTextWatcher) } override fun onPause() { super.onPause() passwordView?.removeTextChangedListener(passwordTextWatcher) - keyFileView?.removeTextChangedListener(keyFileTextWatcher) } private fun verifyPassword(): Boolean { @@ -216,11 +204,11 @@ class AssignMasterKeyDialogFragment : DialogFragment() { if (keyFileCheckBox != null && keyFileCheckBox!!.isChecked) { - UriUtil.parse(keyFileView?.text?.toString())?.let { uri -> + keyFileSelectionView?.uri?.let { uri -> mKeyFile = uri } ?: run { error = true - keyFileTextInputLayout?.error = getString(R.string.error_nokeyfile) + keyFileSelectionView?.error = getString(R.string.error_nokeyfile) } } return error @@ -265,8 +253,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() { ) { uri -> uri?.let { pathUri -> keyFileCheckBox?.isChecked = true - keyFileView?.text = pathUri.toString() - + keyFileSelectionView?.uri = pathUri } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt index 32bfa7ee6..bfa3b948d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt @@ -52,14 +52,22 @@ class OpenFileHelper { this.fragment = context } - inner class OpenFileOnClickViewListener : View.OnClickListener { + inner class OpenFileOnClickViewListener : View.OnClickListener, View.OnLongClickListener { - override fun onClick(v: View) { + private fun onAbstractClick(longClick: Boolean = false) { try { - try { - openActivityWithActionOpenDocument() - } catch(e: Exception) { - openActivityWithActionGetContent() + if (longClick) { + try { + openActivityWithActionGetContent() + } catch (e: Exception) { + openActivityWithActionOpenDocument() + } + } else { + try { + openActivityWithActionOpenDocument() + } catch (e: Exception) { + openActivityWithActionGetContent() + } } } catch (e: Exception) { Log.e(TAG, "Enable to start the file picker activity", e) @@ -68,6 +76,15 @@ class OpenFileHelper { showBrowserDialog() } } + + override fun onClick(v: View) { + onAbstractClick() + } + + override fun onLongClick(v: View?): Boolean { + onAbstractClick(true) + return true + } } @SuppressLint("InlinedApi") diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt index 306a49f86..e04a024fe 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/SearchEntryCursorAdapter.kt @@ -28,8 +28,14 @@ import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.cursor.EntryCursorKDB +import com.kunzisoft.keepass.database.cursor.EntryCursorKDBX import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Entry +import com.kunzisoft.keepass.database.element.Group +import com.kunzisoft.keepass.database.element.database.DatabaseKDB +import com.kunzisoft.keepass.database.element.database.DatabaseKDBX +import com.kunzisoft.keepass.database.search.SearchHelper import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.view.strikeOut @@ -69,8 +75,7 @@ class SearchEntryCursorAdapter(private val context: Context, } override fun bindView(view: View, context: Context, cursor: Cursor) { - - database.getEntryFrom(cursor)?.let { currentEntry -> + getEntryFrom(cursor)?.let { currentEntry -> val viewHolder = view.tag as ViewHolder // Assign image @@ -98,14 +103,46 @@ class SearchEntryCursorAdapter(private val context: Context, } } - private class ViewHolder { - internal var imageViewIcon: ImageView? = null - internal var textViewTitle: TextView? = null - internal var textViewSubTitle: TextView? = null + private fun getEntryFrom(cursor: Cursor): Entry? { + return database.createEntry()?.apply { + database.startManageEntry(this) + entryKDB?.let { entryKDB -> + (cursor as EntryCursorKDB).populateEntry(entryKDB, database.iconFactory) + } + entryKDBX?.let { entryKDBX -> + (cursor as EntryCursorKDBX).populateEntry(entryKDBX, database.iconFactory) + } + database.stopManageEntry(this) + } } override fun runQueryOnBackgroundThread(constraint: CharSequence): Cursor? { - return database.searchEntries(context, constraint.toString()) + return searchEntries(context, constraint.toString()) + } + + private fun searchEntries(context: Context, query: String): Cursor? { + var cursorKDB: EntryCursorKDB? = null + var cursorKDBX: EntryCursorKDBX? = null + + if (database.type == DatabaseKDB.TYPE) + cursorKDB = EntryCursorKDB() + if (database.type == DatabaseKDBX.TYPE) + cursorKDBX = EntryCursorKDBX() + + val searchGroup = database.createVirtualGroupFromSearch(query, SearchHelper.MAX_SEARCH_ENTRY) + if (searchGroup != null) { + // Search in hide entries but not meta-stream + for (entry in searchGroup.getFilteredChildEntries(*Group.ChildFilter.getDefaults(context))) { + entry.entryKDB?.let { + cursorKDB?.addEntry(it) + } + entry.entryKDBX?.let { + cursorKDBX?.addEntry(it) + } + } + } + + return cursorKDB ?: cursorKDBX } fun getEntryFromPosition(position: Int): Entry? { @@ -113,9 +150,14 @@ class SearchEntryCursorAdapter(private val context: Context, val cursor = this.cursor if (cursor.moveToFirst() && cursor.move(position)) { - pwEntry = database.getEntryFrom(cursor) + pwEntry = getEntryFrom(cursor) } return pwEntry } + private class ViewHolder { + internal var imageViewIcon: ImageView? = null + internal var textViewTitle: TextView? = null + internal var textViewSubTitle: TextView? = null + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt index 06f492206..0ae33b401 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillHelper.kt @@ -31,9 +31,17 @@ import android.view.autofill.AutofillManager import android.view.autofill.AutofillValue import android.widget.RemoteViews import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.database.element.icon.IconImage +import com.kunzisoft.keepass.database.search.SearchHelper +import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.model.EntryInfo +import com.kunzisoft.keepass.model.SearchInfo +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.timeout.TimeoutHelper @RequiresApi(api = Build.VERSION_CODES.O) @@ -42,6 +50,7 @@ object AutofillHelper { private const val AUTOFILL_RESPONSE_REQUEST_CODE = 8165 private const val ASSIST_STRUCTURE = AutofillManager.EXTRA_ASSIST_STRUCTURE + const val KEY_SEARCH_INFO = "KEY_SEARCH_INFO" fun retrieveAssistStructure(intent: Intent?): AssistStructure? { intent?.let { @@ -62,11 +71,28 @@ object AutofillHelper { return "" } - private fun buildDataset(context: Context, - entryInfo: EntryInfo, - struct: StructureParser.Result): Dataset? { + internal fun addHeader(responseBuilder: FillResponse.Builder, + packageName: String, + webDomain: String?, + applicationId: String?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (webDomain != null) { + responseBuilder.setHeader(RemoteViews(packageName, R.layout.item_autofill_web_domain).apply { + setTextViewText(R.id.autofill_web_domain_text, webDomain) + }) + } else if (applicationId != null) { + responseBuilder.setHeader(RemoteViews(packageName, R.layout.item_autofill_app_id).apply { + setTextViewText(R.id.autofill_app_id_text, applicationId) + }) + } + } + } + + internal fun buildDataset(context: Context, + entryInfo: EntryInfo, + struct: StructureParser.Result): Dataset? { val title = makeEntryTitle(entryInfo) - val views = newRemoteViews(context.packageName, title) + val views = newRemoteViews(context, title, entryInfo.icon) val builder = Dataset.Builder(views) builder.setId(entryInfo.id) @@ -86,9 +112,16 @@ object AutofillHelper { } /** - * Method to hit when right key is selected + * Build the Autofill response for one entry + */ + fun buildResponse(activity: Activity, entryInfo: EntryInfo) { + buildResponse(activity, ArrayList().apply { add(entryInfo) }) + } + + /** + * Build the Autofill response for many entry */ - fun buildResponseWhenEntrySelected(activity: Activity, entryInfo: EntryInfo) { + fun buildResponse(activity: Activity, entriesInfo: List) { var setResultOk = false activity.intent?.extras?.let { extras -> if (extras.containsKey(ASSIST_STRUCTURE)) { @@ -96,8 +129,9 @@ object AutofillHelper { StructureParser(structure).parse()?.let { result -> // New Response val responseBuilder = FillResponse.Builder() - val dataset = buildDataset(activity, entryInfo, result) - responseBuilder.addDataset(dataset) + entriesInfo.forEach { + responseBuilder.addDataset(buildDataset(activity, it, result)) + } val mReplyIntent = Intent() Log.d(activity.javaClass.name, "Successed Autofill auth.") mReplyIntent.putExtra( @@ -115,12 +149,48 @@ object AutofillHelper { } } + /** + * Utility method to perform actions if item is found or not after an auto search in [database] + */ + fun checkAutoSearchInfo(context: Context, + database: Database, + searchInfo: SearchInfo?, + onItemsFound: (items: List) -> Unit, + onItemNotFound: () -> Unit, + onDatabaseClosed: () -> Unit) { + if (database.loaded && TimeoutHelper.checkTime(context)) { + var searchWithoutUI = false + if (PreferencesUtil.isAutofillAutoSearchEnable(context) + && searchInfo != null) { + // If search provide results + database.createVirtualGroupFromSearch(searchInfo, SearchHelper.MAX_SEARCH_ENTRY)?.let { searchGroup -> + if (searchGroup.getNumberOfChildEntries() > 0) { + searchWithoutUI = true + onItemsFound.invoke( + searchGroup.getChildEntriesInfo(database)) + } + } + } + if (!searchWithoutUI) { + onItemNotFound.invoke() + } + } else { + onDatabaseClosed.invoke() + } + } + /** * Utility method to start an activity with an Autofill for result */ - fun startActivityForAutofillResult(activity: Activity, intent: Intent, assistStructure: AssistStructure) { + fun startActivityForAutofillResult(activity: Activity, + intent: Intent, + assistStructure: AssistStructure, + searchInfo: SearchInfo?) { EntrySelectionHelper.addEntrySelectionModeExtraInIntent(intent) intent.putExtra(ASSIST_STRUCTURE, assistStructure) + searchInfo?.let { + intent.putExtra(KEY_SEARCH_INFO, it) + } activity.startActivityForResult(intent, AUTOFILL_RESPONSE_REQUEST_CODE) } @@ -139,9 +209,18 @@ object AutofillHelper { } } - private fun newRemoteViews(packageName: String, remoteViewsText: String): RemoteViews { - val presentation = RemoteViews(packageName, R.layout.item_autofill_service) - presentation.setTextViewText(R.id.text, remoteViewsText) + private fun newRemoteViews(context: Context, + remoteViewsText: String, + remoteViewsIcon: IconImage? = null): RemoteViews { + val presentation = RemoteViews(context.packageName, R.layout.item_autofill_entry) + presentation.setTextViewText(R.id.autofill_entry_text, remoteViewsText) + if (remoteViewsIcon != null) { + presentation.assignDatabaseIcon(context, + R.id.autofill_entry_icon, + Database.getInstance().drawFactory, + remoteViewsIcon, + ContextCompat.getColor(context, R.color.green)) + } return presentation } } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillLauncherActivity.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillLauncherActivity.kt index 291eda9b1..7f1e749a2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillLauncherActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/AutofillLauncherActivity.kt @@ -31,7 +31,7 @@ import androidx.appcompat.app.AppCompatActivity import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity import com.kunzisoft.keepass.activities.GroupActivity import com.kunzisoft.keepass.database.element.Database -import com.kunzisoft.keepass.timeout.TimeoutHelper +import com.kunzisoft.keepass.model.SearchInfo @RequiresApi(api = Build.VERSION_CODES.O) class AutofillLauncherActivity : AppCompatActivity() { @@ -40,13 +40,31 @@ class AutofillLauncherActivity : AppCompatActivity() { // Pass extra for Autofill (EXTRA_ASSIST_STRUCTURE) val assistStructure = AutofillHelper.retrieveAssistStructure(intent) if (assistStructure != null) { - if (Database.getInstance().loaded && TimeoutHelper.checkTime(this)) - GroupActivity.launchForAutofillResult(this, - assistStructure) - else { - FileDatabaseSelectActivity.launchForAutofillResult(this, - assistStructure) + // Build search param + val searchInfo = SearchInfo().apply { + applicationId = intent.getStringExtra(KEY_SEARCH_APPLICATION_ID) + webDomain = intent.getStringExtra(KEY_SEARCH_DOMAIN) } + // If database is open + AutofillHelper.checkAutoSearchInfo(this, + Database.getInstance(), + searchInfo, + { items -> + // Items found + AutofillHelper.buildResponse(this, items) + finish() + }, + { + // Show the database UI to select the entry + GroupActivity.launchForAutofillResult(this, + assistStructure) + }, + { + // If database not open + FileDatabaseSelectActivity.launchForAutofillResult(this, + assistStructure, searchInfo) + } + ) } else { setResult(Activity.RESULT_CANCELED) finish() @@ -62,10 +80,20 @@ class AutofillLauncherActivity : AppCompatActivity() { companion object { - fun getAuthIntentSenderForResponse(context: Context): IntentSender { - val intent = Intent(context, AutofillLauncherActivity::class.java) + private const val KEY_SEARCH_APPLICATION_ID = "KEY_SEARCH_APPLICATION_ID" + private const val KEY_SEARCH_DOMAIN = "KEY_SEARCH_DOMAIN" + + fun getAuthIntentSenderForResponse(context: Context, + searchInfo: SearchInfo? = null): IntentSender { return PendingIntent.getActivity(context, 0, - intent, PendingIntent.FLAG_CANCEL_CURRENT).intentSender + // Doesn't work with Parcelable (don't know why?) + Intent(context, AutofillLauncherActivity::class.java).apply { + searchInfo?.let { + putExtra(KEY_SEARCH_APPLICATION_ID, it.applicationId) + putExtra(KEY_SEARCH_DOMAIN, it.webDomain) + } + }, + PendingIntent.FLAG_CANCEL_CURRENT).intentSender } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt index 789e9a0a3..04007d902 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/KeeAutofillService.kt @@ -22,31 +22,78 @@ package com.kunzisoft.keepass.autofill import android.os.Build import android.os.CancellationSignal import android.service.autofill.* -import androidx.annotation.RequiresApi import android.util.Log import android.widget.RemoteViews +import androidx.annotation.RequiresApi import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.element.Database +import com.kunzisoft.keepass.model.SearchInfo @RequiresApi(api = Build.VERSION_CODES.O) class KeeAutofillService : AutofillService() { - override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal, + override fun onFillRequest(request: FillRequest, + cancellationSignal: CancellationSignal, callback: FillCallback) { val fillContexts = request.fillContexts val latestStructure = fillContexts[fillContexts.size - 1].structure cancellationSignal.setOnCancelListener { Log.w(TAG, "Cancel autofill.") } - val responseBuilder = FillResponse.Builder() // Check user's settings for authenticating Responses and Datasets. - val parseResult = StructureParser(latestStructure).parse() - parseResult?.allAutofillIds()?.let { autofillIds -> + StructureParser(latestStructure).parse()?.let { parseResult -> + + val searchInfo = SearchInfo().apply { + applicationId = parseResult.applicationId + webDomain = parseResult.domain + } + + AutofillHelper.checkAutoSearchInfo(this, + Database.getInstance(), + searchInfo, + { items -> + val responseBuilder = FillResponse.Builder() + AutofillHelper.addHeader(responseBuilder, packageName, + parseResult.domain, parseResult.applicationId) + items.forEach { + responseBuilder.addDataset(AutofillHelper.buildDataset(this, it, parseResult)) + } + callback.onSuccess(responseBuilder.build()) + }, + { + // Show UI if no search result + showUIForEntrySelection(parseResult, searchInfo, callback) + }, + { + // Show UI if database not open + showUIForEntrySelection(parseResult, searchInfo, callback) + } + ) + } + } + + private fun showUIForEntrySelection(parseResult: StructureParser.Result, + searchInfo: SearchInfo, + callback: FillCallback) { + parseResult.allAutofillIds().let { autofillIds -> if (autofillIds.isNotEmpty()) { // If the entire Autofill Response is authenticated, AuthActivity is used // to generate Response. - val sender = AutofillLauncherActivity.getAuthIntentSenderForResponse(this) - val presentation = RemoteViews(packageName, R.layout.item_autofill_service_unlock) - responseBuilder.setAuthentication(autofillIds, sender, presentation) + val sender = AutofillLauncherActivity.getAuthIntentSenderForResponse(this, + searchInfo) + val responseBuilder = FillResponse.Builder() + val remoteViewsUnlock: RemoteViews = if (!parseResult.domain.isNullOrEmpty()) { + RemoteViews(packageName, R.layout.item_autofill_unlock_web_domain).apply { + setTextViewText(R.id.autofill_web_domain_text, parseResult.domain) + } + } else if (!parseResult.applicationId.isNullOrEmpty()) { + RemoteViews(packageName, R.layout.item_autofill_unlock_app_id).apply { + setTextViewText(R.id.autofill_app_id_text, parseResult.applicationId) + } + } else { + RemoteViews(packageName, R.layout.item_autofill_unlock) + } + responseBuilder.setAuthentication(autofillIds, sender, remoteViewsUnlock) callback.onSuccess(responseBuilder.build()) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt b/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt index c22fe810a..601dd184a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt +++ b/app/src/main/java/com/kunzisoft/keepass/autofill/StructureParser.kt @@ -20,6 +20,7 @@ package com.kunzisoft.keepass.autofill import android.app.assist.AssistStructure import android.os.Build +import android.text.InputType import androidx.annotation.RequiresApi import android.util.Log import android.view.View @@ -36,47 +37,60 @@ internal class StructureParser(private val structure: AssistStructure) { private var usernameCandidate: AutofillId? = null fun parse(): Result? { - result = Result() - result?.apply { - usernameCandidate = null - mainLoop@ for (i in 0 until structure.windowNodeCount) { - val windowNode = structure.getWindowNodeAt(i) - /* - title.add(windowNode.title) - windowNode.rootViewNode.webDomain?.let { - webDomain.add(it) + try { + result = Result() + result?.apply { + usernameCandidate = null + mainLoop@ for (i in 0 until structure.windowNodeCount) { + val windowNode = structure.getWindowNodeAt(i) + applicationId = windowNode.title.toString().split("/")[0] + Log.d(TAG, "Autofill applicationId: $applicationId") + + if (parseViewNode(windowNode.rootViewNode)) + break@mainLoop } - */ - if (parseViewNode(windowNode.rootViewNode)) - break@mainLoop + // If not explicit username field found, add the field just before password field. + if (usernameId == null && passwordId != null && usernameCandidate != null) + usernameId = usernameCandidate } - // If not explicit username field found, add the field just before password field. - if (usernameId == null && passwordId != null && usernameCandidate != null) - usernameId = usernameCandidate - } - // Return the result only if password field is retrieved - return if (result?.passwordId != null) - result - else - null + // Return the result only if password field is retrieved + return if (result?.usernameId != null + && result?.passwordId != null) + result + else + null + } catch (e: Exception) { + return null + } } private fun parseViewNode(node: AssistStructure.ViewNode): Boolean { - if (node.autofillId != null) { - val hints = node.autofillHints - if (hints != null && hints.isNotEmpty()) { - if (parseNodeByAutofillHint(node)) + // Get the domain of a web app + node.webDomain?.let { + result?.domain = it + Log.d(TAG, "Autofill domain: $it") + } + + // Only parse visible nodes + if (node.visibility == View.VISIBLE) { + if (node.autofillId != null + && node.autofillType == View.AUTOFILL_TYPE_TEXT) { + // Parse methods + val hints = node.autofillHints + if (hints != null && hints.isNotEmpty()) { + if (parseNodeByAutofillHint(node)) + return true + } else if (parseNodeByHtmlAttributes(node)) return true - } else { - if (parseNodeByHtmlAttributes(node)) + else if (parseNodeByAndroidInput(node)) + return true + } + // Recursive method to process each node + for (i in 0 until node.childCount) { + if (parseViewNode(node.getChildAt(i))) return true } - } - // Recursive method to process each node - for (i in 0 until node.childCount) { - if (parseViewNode(node.getChildAt(i))) - return true } return false } @@ -85,22 +99,23 @@ internal class StructureParser(private val structure: AssistStructure) { val autofillId = node.autofillId node.autofillHints?.forEach { when { - it.toLowerCase(Locale.ENGLISH) == View.AUTOFILL_HINT_USERNAME - || it.toLowerCase(Locale.ENGLISH) == View.AUTOFILL_HINT_EMAIL_ADDRESS - || it.toLowerCase(Locale.ENGLISH) == View.AUTOFILL_HINT_PHONE -> { + it.equals(View.AUTOFILL_HINT_USERNAME, true) + || it.equals(View.AUTOFILL_HINT_EMAIL_ADDRESS, true) + || it.equals(View.AUTOFILL_HINT_PHONE, true) + || it.equals("usernameOrEmail", true)-> { result?.usernameId = autofillId Log.d(TAG, "Autofill username hint") } - it.toLowerCase(Locale.ENGLISH) == View.AUTOFILL_HINT_PASSWORD - || it.toLowerCase(Locale.ENGLISH).contains("password") -> { + it.equals(View.AUTOFILL_HINT_PASSWORD, true) + || it.contains("password", true) -> { result?.passwordId = autofillId Log.d(TAG, "Autofill password hint") return true } // Ignore autocomplete="off" // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion - it.toLowerCase(Locale.ENGLISH) == "off" || - it.toLowerCase(Locale.ENGLISH) == "on" -> { + it.equals("off", true) || + it.equals("on", true) -> { Log.d(TAG, "Autofill web hint") return parseNodeByHtmlAttributes(node) } @@ -121,15 +136,15 @@ internal class StructureParser(private val structure: AssistStructure) { when (pairAttribute.second.toLowerCase(Locale.ENGLISH)) { "tel", "email" -> { result?.usernameId = autofillId - Log.d(TAG, "Autofill username type: ${node.htmlInfo?.tag} ${node.htmlInfo?.attributes}") + Log.d(TAG, "Autofill username web type: ${node.htmlInfo?.tag} ${node.htmlInfo?.attributes}") } "text" -> { usernameCandidate = autofillId - Log.d(TAG, "Autofill type: ${node.htmlInfo?.tag} ${node.htmlInfo?.attributes}") + Log.d(TAG, "Autofill username candidate web type: ${node.htmlInfo?.tag} ${node.htmlInfo?.attributes}") } "password" -> { result?.passwordId = autofillId - Log.d(TAG, "Autofill password type: ${node.htmlInfo?.tag} ${node.htmlInfo?.attributes}") + Log.d(TAG, "Autofill password web type: ${node.htmlInfo?.tag} ${node.htmlInfo?.attributes}") return true } } @@ -141,8 +156,57 @@ internal class StructureParser(private val structure: AssistStructure) { return false } + private fun parseNodeByAndroidInput(node: AssistStructure.ViewNode): Boolean { + val autofillId = node.autofillId + val inputType = node.inputType + if (inputType and InputType.TYPE_CLASS_TEXT != 0) { + when { + inputType and InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS != 0 -> { + result?.usernameId = autofillId + Log.d(TAG, "Autofill username android type: $inputType") + } + inputType and InputType.TYPE_TEXT_VARIATION_NORMAL != 0 || + inputType and InputType.TYPE_NUMBER_VARIATION_NORMAL != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_PERSON_NAME != 0 -> { + usernameCandidate = autofillId + Log.d(TAG, "Autofill username candidate android type: $inputType") + } + inputType and InputType.TYPE_TEXT_VARIATION_PASSWORD != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD != 0 || + inputType and InputType.TYPE_NUMBER_VARIATION_PASSWORD != 0 -> { + result?.passwordId = autofillId + Log.d(TAG, "Autofill password android type: $inputType") + return true + } + inputType and InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_FILTER != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_PHONETIC != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_URI != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != 0 || + inputType and InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD != 0 -> { + // Type not used + } + else -> { + Log.d(TAG, "Autofill unknown android type: $inputType") + usernameCandidate = autofillId + } + } + } + return false + } + @RequiresApi(api = Build.VERSION_CODES.O) internal class Result { + var applicationId: String? = null + var domain: String? = null + set(value) { + if (field == null) + field = value + } + var usernameId: AutofillId? = null set(value) { if (field == null) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 2679a326e..7bf122546 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -20,15 +20,11 @@ package com.kunzisoft.keepass.database.element import android.content.ContentResolver -import android.content.Context import android.content.res.Resources -import android.database.Cursor import android.net.Uri import android.util.Log import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine import com.kunzisoft.keepass.database.action.node.NodeHandler -import com.kunzisoft.keepass.database.cursor.EntryCursorKDB -import com.kunzisoft.keepass.database.cursor.EntryCursorKDBX import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm import com.kunzisoft.keepass.database.element.database.DatabaseKDB import com.kunzisoft.keepass.database.element.database.DatabaseKDBX @@ -49,6 +45,7 @@ import com.kunzisoft.keepass.database.file.output.DatabaseOutputKDB import com.kunzisoft.keepass.database.file.output.DatabaseOutputKDBX import com.kunzisoft.keepass.database.search.SearchHelper import com.kunzisoft.keepass.icons.IconDrawableFactory +import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.stream.readBytes4ToInt import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.utils.SingletonHolder @@ -137,6 +134,9 @@ class Database { val version: String get() = mDatabaseKDB?.version ?: mDatabaseKDBX?.version ?: "-" + val type: Class<*>? + get() = mDatabaseKDB?.javaClass ?: mDatabaseKDBX?.javaClass + val allowDataCompression: Boolean get() = mDatabaseKDBX != null @@ -397,54 +397,17 @@ class Database { false } - @JvmOverloads - fun search(str: String, max: Int = Integer.MAX_VALUE): Group? { - return mSearchHelper?.search(this, str, max) + fun createVirtualGroupFromSearch(searchQuery: String, max: Int = Integer.MAX_VALUE): Group? { + return mSearchHelper?.createVirtualGroupWithSearchResult(this, searchQuery, max) } - fun searchEntries(context: Context, query: String): Cursor? { - - var cursorKDB: EntryCursorKDB? = null - var cursorKDBX: EntryCursorKDBX? = null - - if (mDatabaseKDB != null) - cursorKDB = EntryCursorKDB() - if (mDatabaseKDBX != null) - cursorKDBX = EntryCursorKDBX() - - val searchResult = search(query, SearchHelper.MAX_SEARCH_ENTRY) - if (searchResult != null) { - // Search in hide entries but not meta-stream - for (entry in searchResult.getFilteredChildEntries(*Group.ChildFilter.getDefaults(context))) { - entry.entryKDB?.let { - cursorKDB?.addEntry(it) - } - entry.entryKDBX?.let { - cursorKDBX?.addEntry(it) - } - } - } - - return cursorKDB ?: cursorKDBX - } - - fun getEntryFrom(cursor: Cursor): Entry? { - val iconFactory = mDatabaseKDB?.iconFactory ?: mDatabaseKDBX?.iconFactory ?: IconImageFactory() - - return createEntry()?.apply { - startManageEntry(this) - mDatabaseKDB?.let { - entryKDB?.let { entryKDB -> - (cursor as EntryCursorKDB).populateEntry(entryKDB, iconFactory) - } - } - mDatabaseKDBX?.let { - entryKDBX?.let { entryKDBX -> - (cursor as EntryCursorKDBX).populateEntry(entryKDBX, iconFactory) - } - } - stopManageEntry(this) - } + fun createVirtualGroupFromSearch(searchInfo: SearchInfo, max: Int = Integer.MAX_VALUE): Group? { + val query = (if (searchInfo.webDomain != null) + searchInfo.webDomain + else + searchInfo.applicationId) + ?: return null + return mSearchHelper?.createVirtualGroupWithSearchResult(this, query, max) } @Throws(DatabaseOutputException::class) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt index 55c8790fb..9a4edc9d5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt @@ -398,6 +398,7 @@ class Entry : Node, EntryVersionedInterface { database?.startManageEntry(this) entryInfo.id = nodeId.toString() entryInfo.title = title + entryInfo.icon = icon entryInfo.username = username entryInfo.password = password entryInfo.url = url diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt index 139f3cb28..8606b19f6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Group.kt @@ -28,6 +28,7 @@ import com.kunzisoft.keepass.database.element.group.GroupVersionedInterface import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImageStandard import com.kunzisoft.keepass.database.element.node.* +import com.kunzisoft.keepass.model.EntryInfo import com.kunzisoft.keepass.settings.PreferencesUtil import java.util.* import kotlin.collections.ArrayList @@ -251,6 +252,14 @@ class Group : Node, GroupVersionedInterface { ArrayList() } + fun getChildEntriesInfo(database: Database): List { + val entriesInfo = ArrayList() + getChildEntries().forEach { entry -> + entriesInfo.add(entry.getEntryInfo(database)) + } + return entriesInfo + } + fun getFilteredChildEntries(vararg filter: ChildFilter): List { val withoutMetaStream = filter.contains(ChildFilter.META_STREAM) val showExpiredEntries = !filter.contains(ChildFilter.EXPIRED) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt index 5dbaeac98..21f0c66c2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDB.kt @@ -26,7 +26,6 @@ import com.kunzisoft.keepass.database.element.entry.EntryKDB import com.kunzisoft.keepass.database.element.group.GroupKDB import com.kunzisoft.keepass.database.element.node.NodeIdInt import com.kunzisoft.keepass.database.element.node.NodeIdUUID -import com.kunzisoft.keepass.database.element.node.NodeVersioned import com.kunzisoft.keepass.database.element.security.EncryptionAlgorithm import com.kunzisoft.keepass.stream.NullOutputStream import java.io.IOException @@ -262,6 +261,7 @@ class DatabaseKDB : DatabaseVersioned() { } companion object { + val TYPE = DatabaseKDB::class.java const val BACKUP_FOLDER_TITLE = "Backup" private const val BACKUP_FOLDER_UNDEFINED_ID = -1 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt index 591ebe481..817e6d09c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/database/DatabaseKDBX.kt @@ -29,7 +29,8 @@ import com.kunzisoft.keepass.crypto.engine.CipherEngine import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine import com.kunzisoft.keepass.crypto.keyDerivation.KdfFactory import com.kunzisoft.keepass.crypto.keyDerivation.KdfParameters -import com.kunzisoft.keepass.database.element.* +import com.kunzisoft.keepass.database.element.DateInstant +import com.kunzisoft.keepass.database.element.DeletedObject import com.kunzisoft.keepass.database.element.database.DatabaseKDB.Companion.BACKUP_FOLDER_TITLE import com.kunzisoft.keepass.database.element.entry.EntryKDBX import com.kunzisoft.keepass.database.element.group.GroupKDBX @@ -551,6 +552,7 @@ class DatabaseKDBX : DatabaseVersioned { } companion object { + val TYPE = DatabaseKDBX::class.java private val TAG = DatabaseKDBX::class.java.name private const val DEFAULT_HISTORY_MAX_ITEMS = 10 // -1 unlimited diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt index 929df37cb..5135ca3db 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt @@ -26,7 +26,6 @@ import com.kunzisoft.keepass.database.element.Group import com.kunzisoft.keepass.database.search.iterator.EntrySearchStringIterator import com.kunzisoft.keepass.database.search.iterator.EntrySearchStringIteratorKDB import com.kunzisoft.keepass.database.search.iterator.EntrySearchStringIteratorKDBX -import java.util.* class SearchHelper(private val isOmitBackup: Boolean) { @@ -36,22 +35,19 @@ class SearchHelper(private val isOmitBackup: Boolean) { private var incrementEntry = 0 - fun search(database: Database, qStr: String, max: Int): Group? { + fun createVirtualGroupWithSearchResult(database: Database, searchQuery: String, max: Int): Group? { val searchGroup = database.createGroup() - searchGroup?.title = "\"" + qStr + "\"" + searchGroup?.title = "\"" + searchQuery + "\"" // Search all entries - val loc = Locale.getDefault() - val finalQStr = qStr.toLowerCase(loc) - incrementEntry = 0 database.rootGroup?.doForEachChild( object : NodeHandler() { override fun operate(node: Entry): Boolean { if (incrementEntry >= max) return false - if (entryContainsString(node, finalQStr, loc)) { + if (entryContainsString(node, searchQuery)) { searchGroup?.addChildEntry(node) incrementEntry++ } @@ -73,7 +69,7 @@ class SearchHelper(private val isOmitBackup: Boolean) { return searchGroup } - private fun entryContainsString(entry: Entry, searchString: String, locale: Locale): Boolean { + private fun entryContainsString(entry: Entry, searchString: String): Boolean { // Entry don't contains string if the search string is empty if (searchString.isEmpty()) @@ -90,11 +86,10 @@ class SearchHelper(private val isOmitBackup: Boolean) { iterator?.let { while (it.hasNext()) { - val str = it.next() - if (str.isNotEmpty()) { - if (str.toLowerCase(locale).contains(searchString)) { + val currentString = it.next() + if (currentString.isNotEmpty() + && currentString.contains(searchString, true)) { return true - } } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorKDB.kt index 7510e4b73..a881ea054 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/search/iterator/EntrySearchStringIteratorKDB.kt @@ -39,7 +39,7 @@ constructor(private val mEntry: EntryKDB, title -> mEntry.title url -> mEntry.url username -> mEntry.username - comment -> mEntry.notes + notes -> mEntry.notes else -> "" } } @@ -73,7 +73,7 @@ constructor(private val mEntry: EntryKDB, title -> mSearchParameters.searchInTitles url -> mSearchParameters.searchInUrls username -> mSearchParameters.searchInUserNames - comment -> mSearchParameters.searchInNotes + notes -> mSearchParameters.searchInNotes else -> true } @@ -88,7 +88,7 @@ constructor(private val mEntry: EntryKDB, private const val title = 0 private const val url = 1 private const val username = 2 - private const val comment = 3 + private const val notes = 3 private const val maxEntries = 4 } diff --git a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt index d8a696e98..ac93bc8f9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt +++ b/app/src/main/java/com/kunzisoft/keepass/icons/IconDrawableFactory.kt @@ -22,16 +22,16 @@ package com.kunzisoft.keepass.icons import android.content.Context import android.content.res.ColorStateList import android.content.res.Resources -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.Color +import android.graphics.* import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable -import androidx.core.content.res.ResourcesCompat -import androidx.core.widget.ImageViewCompat import android.util.Log import android.widget.ImageView +import android.widget.RemoteViews +import androidx.core.content.res.ResourcesCompat +import androidx.core.graphics.drawable.toBitmap +import androidx.core.widget.ImageViewCompat import com.kunzisoft.keepass.R import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.database.element.icon.IconImageCustom @@ -70,6 +70,23 @@ class IconDrawableFactory { } } + /** + * Utility method to assign a drawable to a RemoteView and tint it + */ + fun assignDrawableToRemoteViews(superDrawable: SuperDrawable, + remoteViews: RemoteViews, + imageId: Int, + tintColor: Int = Color.BLACK) { + val bitmap = superDrawable.drawable.toBitmap() + // Tint bitmap if it's not a custom icon + if (!superDrawable.custom) { + Canvas(bitmap).drawBitmap(bitmap, 0.0F, 0.0F, Paint().apply { + colorFilter = PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN) + }) + } + remoteViews.setImageViewBitmap(imageId, bitmap) + } + /** * Get the [SuperDrawable] [icon] (from cache, or build it and add it to the cache if not exists yet), then [tint] it with [tintColor] if needed */ @@ -233,7 +250,6 @@ class IconDrawableFactory { */ fun ImageView.assignDefaultDatabaseIcon(iconFactory: IconDrawableFactory, tintColor: Int = Color.WHITE) { IconPackChooser.getSelectedIconPack(context)?.let { selectedIconPack -> - iconFactory.assignDrawableToImageView( iconFactory.getIconSuperDrawable(context, selectedIconPack.defaultIconId, @@ -249,9 +265,10 @@ fun ImageView.assignDefaultDatabaseIcon(iconFactory: IconDrawableFactory, tintCo /** * Assign a database [icon] to an ImageView and tint it with [tintColor] if needed */ -fun ImageView.assignDatabaseIcon(iconFactory: IconDrawableFactory, icon: IconImage, tintColor: Int = Color.WHITE) { +fun ImageView.assignDatabaseIcon(iconFactory: IconDrawableFactory, + icon: IconImage, + tintColor: Int = Color.WHITE) { IconPackChooser.getSelectedIconPack(context)?.let { selectedIconPack -> - iconFactory.assignDrawableToImageView( iconFactory.getIconSuperDrawable(context, icon, @@ -263,3 +280,19 @@ fun ImageView.assignDatabaseIcon(iconFactory: IconDrawableFactory, icon: IconIma tintColor) } } + +fun RemoteViews.assignDatabaseIcon(context: Context, + imageId: Int, + iconFactory: IconDrawableFactory, + icon: IconImage, + tintColor: Int = Color.BLACK) { + iconFactory.assignDrawableToRemoteViews( + iconFactory.getIconSuperDrawable(context, + icon, + 24, + true, + tintColor), + this, + imageId, + tintColor) +} diff --git a/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt b/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt index a4f4986fc..8edec2238 100644 --- a/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt +++ b/app/src/main/java/com/kunzisoft/keepass/model/EntryInfo.kt @@ -21,6 +21,7 @@ package com.kunzisoft.keepass.model import android.os.Parcel import android.os.Parcelable +import com.kunzisoft.keepass.database.element.icon.IconImage import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD import java.util.* @@ -29,6 +30,7 @@ class EntryInfo : Parcelable { var id: String = "" var title: String = "" + var icon: IconImage? = null var username: String = "" var password: String = "" var url: String = "" @@ -41,6 +43,7 @@ class EntryInfo : Parcelable { private constructor(parcel: Parcel) { id = parcel.readString() ?: id title = parcel.readString() ?: title + icon = parcel.readParcelable(IconImage::class.java.classLoader) username = parcel.readString() ?: username password = parcel.readString() ?: password url = parcel.readString() ?: url @@ -56,6 +59,7 @@ class EntryInfo : Parcelable { override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(id) parcel.writeString(title) + parcel.writeParcelable(icon, flags) parcel.writeString(username) parcel.writeString(password) parcel.writeString(url) diff --git a/app/src/main/java/com/kunzisoft/keepass/model/SearchInfo.kt b/app/src/main/java/com/kunzisoft/keepass/model/SearchInfo.kt new file mode 100644 index 000000000..8ad6e99f4 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/model/SearchInfo.kt @@ -0,0 +1,42 @@ +package com.kunzisoft.keepass.model + +import android.os.Parcel +import android.os.Parcelable + +class SearchInfo : Parcelable { + + var applicationId: String? = null + var webDomain: String? = null + + constructor() + + private constructor(parcel: Parcel) { + val readAppId = parcel.readString() + applicationId = if (readAppId.isNullOrEmpty()) null else readAppId + val readDomain = parcel.readString() + webDomain = if (readDomain.isNullOrEmpty()) null else readDomain + } + + override fun describeContents(): Int { + return 0 + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(applicationId ?: "") + parcel.writeString(webDomain ?: "") + } + + companion object { + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): SearchInfo { + return SearchInfo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt b/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt index 875f98097..80587a453 100644 --- a/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt +++ b/app/src/main/java/com/kunzisoft/keepass/otp/OtpEntryFields.kt @@ -74,7 +74,7 @@ object OtpEntryFields { // [^&=\s]+=[^&=\s]+(&[^&=\s]+=[^&=\s]+)* private const val validKeyValue = "[^&=\\s]+" private const val validKeyValuePair = "$validKeyValue=$validKeyValue" - private const val validKeyValueRegex = "$validKeyValuePair&($validKeyValuePair)*" + private const val validKeyValueRegex = "$validKeyValuePair(&$validKeyValuePair)*" /** * Parse fields of an entry to retrieve an OtpElement @@ -243,21 +243,18 @@ object OtpEntryFields { val plainText = getField(OTP_FIELD) if (plainText != null && plainText.isNotEmpty()) { if (Pattern.matches(validKeyValueRegex, plainText)) { - try { + return try { // KeeOtp string format val query = breakDownKeyValuePairs(plainText) - var secretString = query[SEED_KEY] - if (secretString == null) - secretString = "" - otpElement.setBase32Secret(secretString) - otpElement.digits = query[DIGITS_KEY]?.toIntOrNull() ?: OTP_DEFAULT_DIGITS - otpElement.period = query[STEP_KEY]?.toIntOrNull() ?: TOTP_DEFAULT_PERIOD + otpElement.setBase32Secret(query[SEED_KEY] ?: "") + otpElement.digits = query[DIGITS_KEY]?.toIntOrNull() ?: OTP_DEFAULT_DIGITS + otpElement.period = query[STEP_KEY]?.toIntOrNull() ?: TOTP_DEFAULT_PERIOD - otpElement.type = OtpType.TOTP - return true + otpElement.type = OtpType.TOTP + true } catch (exception: Exception) { - return false + false } } else { // Malformed diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/AutofillSettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/AutofillSettingsActivity.kt new file mode 100644 index 000000000..8279bf6ad --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/settings/AutofillSettingsActivity.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2019 Jeremy Jamet / Kunzisoft. + * + * This file is part of KeePassDX. + * + * KeePassDX is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * KeePassDX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KeePassDX. If not, see . + * + */ +package com.kunzisoft.keepass.settings + +import android.os.Bundle +import android.view.MenuItem +import androidx.appcompat.widget.Toolbar +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.activities.stylish.StylishActivity + +class AutofillSettingsActivity : StylishActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_toolbar) + val toolbar = findViewById(R.id.toolbar) + toolbar.setTitle(R.string.autofill_preference_title) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction() + .replace(R.id.fragment_container, AutofillSettingsFragment()) + .commit() + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> onBackPressed() + } + + return super.onOptionsItemSelected(item) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/AutofillSettingsFragment.kt similarity index 64% rename from app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.kt rename to app/src/main/java/com/kunzisoft/keepass/settings/AutofillSettingsFragment.kt index 934a785f1..96558b1b4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsAutofillActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/AutofillSettingsFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019 Jeremy Jamet / Kunzisoft. + * Copyright 2020 Jeremy Jamet / Kunzisoft. * * This file is part of KeePassDX. * @@ -20,16 +20,14 @@ package com.kunzisoft.keepass.settings import android.os.Bundle -import androidx.fragment.app.Fragment +import androidx.preference.PreferenceFragmentCompat -class SettingsAutofillActivity : SettingsActivity() { +import com.kunzisoft.keepass.R - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - mTimeoutEnable = false - } +class AutofillSettingsFragment : PreferenceFragmentCompat() { - override fun retrieveMainFragment(): Fragment { - return NestedSettingsFragment.newInstance(NestedSettingsFragment.Screen.FORM_FILLING) + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + // Load the preferences from an XML resource + setPreferencesFromResource(R.xml.preferences_autofill, rootKey) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.kt b/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsActivity.kt similarity index 92% rename from app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.kt rename to app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsActivity.kt index 67f8df4cf..98a5ae62a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettings.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsActivity.kt @@ -26,7 +26,7 @@ import android.view.MenuItem import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.stylish.StylishActivity -class MagikIMESettings : StylishActivity() { +class MagikeyboardSettingsActivity : StylishActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -39,7 +39,7 @@ class MagikIMESettings : StylishActivity() { if (savedInstanceState == null) { supportFragmentManager.beginTransaction() - .replace(R.id.fragment_container, MagikIMESettingsFragment()) + .replace(R.id.fragment_container, MagikeyboardSettingsFragment()) .commit() } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsFragment.kt similarity index 94% rename from app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.kt rename to app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsFragment.kt index 594c59c28..e087a7a82 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikIMESettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsFragment.kt @@ -24,7 +24,7 @@ import androidx.preference.PreferenceFragmentCompat import com.kunzisoft.keepass.R -class MagikIMESettingsFragment : PreferenceFragmentCompat() { +class MagikeyboardSettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { // Load the preferences from an XML resource diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt index 3f1b0860d..04bdea04a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MainPreferenceFragment.kt @@ -24,7 +24,6 @@ import android.os.Bundle import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.kunzisoft.keepass.R -import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.database.element.Database class MainPreferenceFragment : PreferenceFragmentCompat() { diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt index 6697c541c..2f0e8b4b4 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt @@ -160,17 +160,22 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { } findPreference(getString(R.string.magic_keyboard_preference_key))?.setOnPreferenceClickListener { - startActivity(Intent(context, MagikIMESettings::class.java)) + startActivity(Intent(context, MagikeyboardSettingsActivity::class.java)) false } - findPreference(getString(R.string.clipboard_explanation_key))?.setOnPreferenceClickListener { - UriUtil.gotoUrl(context!!, R.string.clipboard_explanation_url) + findPreference(getString(R.string.autofill_explanation_key))?.setOnPreferenceClickListener { + UriUtil.gotoUrl(context!!, R.string.autofill_explanation_url) false } - findPreference(getString(R.string.autofill_explanation_key))?.setOnPreferenceClickListener { - UriUtil.gotoUrl(context!!, R.string.autofill_explanation_url) + findPreference(getString(R.string.settings_autofill_key))?.setOnPreferenceClickListener { + startActivity(Intent(context, AutofillSettingsActivity::class.java)) + false + } + + findPreference(getString(R.string.clipboard_explanation_key))?.setOnPreferenceClickListener { + UriUtil.gotoUrl(context!!, R.string.clipboard_explanation_url) false } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt index bb52f4ca2..cdd94e5ea 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt @@ -31,7 +31,6 @@ import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper -import com.kunzisoft.keepass.activities.lock.lock import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm @@ -551,14 +550,10 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() { val settingActivity = activity as SettingsActivity? - when (item.itemId) { - R.id.menu_lock -> { - settingActivity?.lock() - return true - } + return when (item.itemId) { R.id.menu_save_database -> { settingActivity?.mProgressDialogThread?.startDatabaseSave(!mDatabaseReadOnly) - return true + true } else -> { @@ -566,7 +561,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() { settingActivity?.let { MenuUtil.onDefaultMenuOptionsItemSelected(it, item, mDatabaseReadOnly, true) } - return super.onOptionsItemSelected(item) + super.onOptionsItemSelected(item) } } } 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 be7fea957..c3e01fd44 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -199,6 +199,12 @@ object PreferencesUtil { context.resources.getBoolean(R.bool.lock_database_back_root_default)) } + fun showLockDatabaseButton(context: Context): Boolean { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.lock_database_show_button_key), + context.resources.getBoolean(R.bool.lock_database_show_button_default)) + } + fun isAutoSaveDatabaseEnabled(context: Context): Boolean { val prefs = PreferenceManager.getDefaultSharedPreferences(context) return prefs.getBoolean(context.getString(R.string.enable_auto_save_database_key), @@ -348,4 +354,10 @@ object PreferencesUtil { return prefs.getBoolean(context.getString(R.string.keyboard_key_sound_key), context.resources.getBoolean(R.bool.keyboard_key_sound_default)) } + + fun isAutofillAutoSearchEnable(context: Context): Boolean { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + return prefs.getBoolean(context.getString(R.string.autofill_auto_search_key), + context.resources.getBoolean(R.bool.autofill_auto_search_default)) + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index f2099c35a..b097db221 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -26,6 +26,8 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.MenuItem +import android.view.View +import android.widget.ImageView import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.Fragment @@ -47,6 +49,7 @@ open class SettingsActivity private var coordinatorLayout: CoordinatorLayout? = null private var toolbar: Toolbar? = null + private var lockView: View? = null companion object { @@ -84,6 +87,11 @@ open class SettingsActivity setSupportActionBar(toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) + lockView = findViewById(R.id.lock_button) + lockView?.setOnClickListener { + lockAndExit() + } + if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .add(R.id.fragment_container, retrieveMainFragment()) @@ -154,6 +162,23 @@ open class SettingsActivity keyFile: Uri?) { } + private fun hideOrShowLockButton(key: NestedSettingsFragment.Screen) { + if (PreferencesUtil.showLockDatabaseButton(this)) { + when (key) { + NestedSettingsFragment.Screen.DATABASE, + NestedSettingsFragment.Screen.DATABASE_MASTER_KEY, + NestedSettingsFragment.Screen.DATABASE_SECURITY -> { + lockView?.visibility = View.VISIBLE + } + else -> { + lockView?.visibility = View.GONE + } + } + } else { + lockView?.visibility = View.GONE + } + } + override fun onBackPressed() { // this if statement is necessary to navigate through nested and main fragments if (supportFragmentManager.backStackEntryCount == 0) { @@ -162,6 +187,7 @@ open class SettingsActivity supportFragmentManager.popBackStack() } toolbar?.setTitle(R.string.settings) + hideOrShowLockButton(NestedSettingsFragment.Screen.APPLICATION) } private fun replaceFragment(key: NestedSettingsFragment.Screen) { @@ -173,6 +199,7 @@ open class SettingsActivity .commit() toolbar?.title = NestedSettingsFragment.retrieveTitle(resources, key) + hideOrShowLockButton(key) } override fun onNestedPreferenceSelected(key: NestedSettingsFragment.Screen) { diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt index 3b8d3f727..16dd0067f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt @@ -20,6 +20,7 @@ package com.kunzisoft.keepass.view import android.content.Context import android.graphics.Color +import android.text.util.Linkify import android.util.AttributeSet import android.view.LayoutInflater import android.view.View @@ -71,8 +72,8 @@ class EntryContentsView @JvmOverloads constructor(context: Context, private val urlContainerView: View private val urlView: TextView - private val commentContainerView: View - private val commentView: TextView + private val notesContainerView: View + private val notesView: TextView private val extrasContainerView: View private val extrasView: ViewGroup @@ -120,8 +121,8 @@ class EntryContentsView @JvmOverloads constructor(context: Context, urlContainerView = findViewById(R.id.entry_url_container) urlView = findViewById(R.id.entry_url) - commentContainerView = findViewById(R.id.entry_notes_container) - commentView = findViewById(R.id.entry_notes) + notesContainerView = findViewById(R.id.entry_notes_container) + notesView = findViewById(R.id.entry_notes) extrasContainerView = findViewById(R.id.extra_strings_container) extrasView = findViewById(R.id.extra_strings) @@ -289,15 +290,17 @@ class EntryContentsView @JvmOverloads constructor(context: Context, fun assignComment(comment: String?) { if (comment != null && comment.isNotEmpty()) { - commentContainerView.visibility = View.VISIBLE - commentView.apply { + notesContainerView.visibility = View.VISIBLE + notesView.apply { text = comment if (fontInVisibility) applyFontVisibility() } - + try { + Linkify.addLinks(notesView, Linkify.ALL) + } catch (e: Exception) {} } else { - commentContainerView.visibility = View.GONE + notesContainerView.visibility = View.GONE } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt index a80ffa9a2..0773d31ff 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditContentsView.kt @@ -200,12 +200,16 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context, val customFieldsArray = ArrayList() // Add extra fields from views entryExtraFieldsContainer.let { - for (i in 0 until it.childCount) { - val view = it.getChildAt(i) as EntryEditCustomField - val key = view.label - val value = view.value - val protect = view.isProtected - customFieldsArray.add(Field(key, ProtectedString(protect, value))) + try { + for (i in 0 until it.childCount) { + val view = it.getChildAt(i) as EntryEditCustomField + val key = view.label + val value = view.value + val protect = view.isProtected + customFieldsArray.add(Field(key, ProtectedString(protect, value))) + } + } catch (exception: Exception) { + // Extra field container contains another type of view } } return customFieldsArray @@ -215,11 +219,14 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context, * Add a new view to fill in the information of the customized field and focus it */ fun addEmptyCustomField() { - val entryEditCustomField = EntryEditCustomField(context).apply { - setFontVisibility(fontInVisibility) - requestFocus() + // Fix current custom field before adding a new one + if (isValid()) { + val entryEditCustomField = EntryEditCustomField(context).apply { + setFontVisibility(fontInVisibility) + requestFocus() + } + entryExtraFieldsContainer.addView(entryEditCustomField) } - entryExtraFieldsContainer.addView(entryEditCustomField) } /** @@ -254,26 +261,34 @@ class EntryEditContentsView @JvmOverloads constructor(context: Context, * @return ErrorValidation An error with a message or a validation without message */ fun isValid(): Boolean { - var isValid = true - // Validate password if (entryPasswordView.text.toString() != entryConfirmationPasswordView.text.toString()) { entryPasswordLayoutView.error = context.getString(R.string.error_pass_match) - isValid = false + return false } else { entryPasswordLayoutView.error = null } // Validate extra fields entryExtraFieldsContainer.let { - for (i in 0 until it.childCount) { - val entryEditCustomField = it.getChildAt(i) as EntryEditCustomField - if (!entryEditCustomField.isValid()) { - isValid = false + try { + val customFieldLabelSet = HashSet() + for (i in 0 until it.childCount) { + val entryEditCustomField = it.getChildAt(i) as EntryEditCustomField + if (customFieldLabelSet.contains(entryEditCustomField.label)) { + entryEditCustomField.setError(R.string.error_label_exists) + return false + } + customFieldLabelSet.add(entryEditCustomField.label) + if (!entryEditCustomField.isValid()) { + return false + } } + } catch (exception: Exception) { + return false } } - return isValid + return true } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.kt index ba3872cc0..b9e9a1bc6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryEditCustomField.kt @@ -30,6 +30,7 @@ import android.widget.CompoundButton import android.widget.EditText import android.widget.RelativeLayout import android.widget.TextView +import androidx.annotation.StringRes import com.kunzisoft.keepass.R import com.kunzisoft.keepass.database.element.security.ProtectedString @@ -84,14 +85,20 @@ class EntryEditCustomField @JvmOverloads constructor(context: Context, fun isValid(): Boolean { // Validate extra field if (label.isEmpty()) { - labelLayoutView.error = context.getString(R.string.error_string_key) + setError(R.string.error_string_key) return false } else { - labelLayoutView.error = null + setError(null) } return true } + fun setError(@StringRes errorId: Int?) { + labelLayoutView.error = if (errorId == null) null else { + context.getString(errorId) + } + } + fun setFontVisibility(applyFontVisibility: Boolean) { if (applyFontVisibility) valueView.applyFontVisibility() diff --git a/app/src/main/java/com/kunzisoft/keepass/view/KeyFileSelectionView.kt b/app/src/main/java/com/kunzisoft/keepass/view/KeyFileSelectionView.kt new file mode 100644 index 000000000..26340b98d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/view/KeyFileSelectionView.kt @@ -0,0 +1,60 @@ +package com.kunzisoft.keepass.view + +import android.content.Context +import android.net.Uri +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.ImageView +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.documentfile.provider.DocumentFile +import com.google.android.material.textfield.TextInputLayout +import com.kunzisoft.keepass.R + +class KeyFileSelectionView @JvmOverloads constructor(context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0) + : ConstraintLayout(context, attrs, defStyle) { + + private var mUri: Uri? = null + + private val keyFileNameInputLayout: TextInputLayout + private val keyFileNameView: TextView + private val keyFileOpenView: ImageView + + init { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater? + inflater?.inflate(R.layout.view_keyfile_selection, this) + + keyFileNameInputLayout = findViewById(R.id.input_entry_keyfile) + keyFileNameView = findViewById(R.id.keyfile_name) + keyFileOpenView = findViewById(R.id.keyfile_open_button) + } + + override fun setOnClickListener(l: OnClickListener?) { + super.setOnClickListener(l) + keyFileNameView.setOnClickListener(l) + } + + override fun setOnLongClickListener(l: OnLongClickListener?) { + super.setOnLongClickListener(l) + keyFileNameView.setOnLongClickListener(l) + } + + var error: CharSequence? + get() = keyFileNameInputLayout.error + set(value) { + keyFileNameInputLayout.error = value + } + + var uri: Uri? + get() { + return mUri + } + set(value) { + mUri = value + keyFileNameView.text = value?.let { + DocumentFile.fromSingleUri(context, value)?.name ?: "" + } ?: "" + } +} \ No newline at end of file diff --git a/app/src/main/res/color/background_special_button_color.xml b/app/src/main/res/color/background_special_button_color.xml new file mode 100644 index 000000000..b428a4d71 --- /dev/null +++ b/app/src/main/res/color/background_special_button_color.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/btn_special_start_bottom_background.xml b/app/src/main/res/drawable-v21/btn_special_start_bottom_background.xml new file mode 100644 index 000000000..7c5856259 --- /dev/null +++ b/app/src/main/res/drawable-v21/btn_special_start_bottom_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/btn_special_start_bottom_stroke.xml b/app/src/main/res/drawable-v21/btn_special_start_bottom_stroke.xml new file mode 100644 index 000000000..db9860cbd --- /dev/null +++ b/app/src/main/res/drawable-v21/btn_special_start_bottom_stroke.xml @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_special_start_bottom_background.xml b/app/src/main/res/drawable/btn_special_start_bottom_background.xml new file mode 100644 index 000000000..815abdf2d --- /dev/null +++ b/app/src/main/res/drawable/btn_special_start_bottom_background.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_special_start_bottom_stroke.xml b/app/src/main/res/drawable/btn_special_start_bottom_stroke.xml new file mode 100644 index 000000000..0610c0a85 --- /dev/null +++ b/app/src/main/res/drawable/btn_special_start_bottom_stroke.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_apps_white_24dp.xml b/app/src/main/res/drawable/ic_apps_white_24dp.xml new file mode 100644 index 000000000..373f7752b --- /dev/null +++ b/app/src/main/res/drawable/ic_apps_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_web_white_24dp.xml b/app/src/main/res/drawable/ic_web_white_24dp.xml new file mode 100644 index 000000000..880e42770 --- /dev/null +++ b/app/src/main/res/drawable/ic_web_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_entry.xml b/app/src/main/res/layout/activity_entry.xml index 50a14b69a..16f717df0 100644 --- a/app/src/main/res/layout/activity_entry.xml +++ b/app/src/main/res/layout/activity_entry.xml @@ -20,6 +20,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_entry_edit.xml b/app/src/main/res/layout/activity_entry_edit.xml index 103b15fcf..ae6d82ea6 100644 --- a/app/src/main/res/layout/activity_entry_edit.xml +++ b/app/src/main/res/layout/activity_entry_edit.xml @@ -102,6 +102,12 @@ + + - - - - + + + app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> + android:paddingBottom="48dp"> + - - - - + + - - + android:layout_gravity="center" + android:src="@drawable/ic_folder_open_white_24dp" + app:tint="?android:attr/textColorHintInverse" + android:contentDescription="@string/about"/> + - - - + + - + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_group.xml b/app/src/main/res/layout/activity_group.xml index cd10e56ef..5cfdf3803 100644 --- a/app/src/main/res/layout/activity_group.xml +++ b/app/src/main/res/layout/activity_group.xml @@ -140,12 +140,19 @@ android:background="?android:attr/windowBackground" /> + + + app:layout_anchorGravity="end|bottom" /> - - - - - - - - + android:importantForAutofill="no" /> + @@ -239,7 +214,7 @@ android:paddingEnd="24dp" android:paddingRight="24dp" style="@style/KeepassDXStyle.TextAppearance.TinyText" - android:text="@string/warning_database_read_only" + android:text="@string/warning_database_link_revoked" android:textColor="?attr/textColorInverse" android:background="?attr/colorAccent" app:layout_constraintBottom_toTopOf="@+id/activity_password_info_delimiter" diff --git a/app/src/main/res/layout/activity_toolbar.xml b/app/src/main/res/layout/activity_toolbar.xml index 38a889f3e..31e6b6f11 100644 --- a/app/src/main/res/layout/activity_toolbar.xml +++ b/app/src/main/res/layout/activity_toolbar.xml @@ -17,8 +17,9 @@ You should have received a copy of the GNU General Public License along with KeePassDX. If not, see . --> - + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_list_nodes.xml b/app/src/main/res/layout/fragment_list_nodes.xml index afd02ca2f..3eef702c8 100644 --- a/app/src/main/res/layout/fragment_list_nodes.xml +++ b/app/src/main/res/layout/fragment_list_nodes.xml @@ -44,6 +44,7 @@ diff --git a/app/src/main/res/layout/fragment_set_password.xml b/app/src/main/res/layout/fragment_set_password.xml index 0f218f34e..5915bec0d 100644 --- a/app/src/main/res/layout/fragment_set_password.xml +++ b/app/src/main/res/layout/fragment_set_password.xml @@ -109,44 +109,14 @@ android:layout_height="wrap_content" android:text="@string/entry_keyfile"/> - - - - - - - - + android:layout_height="wrap_content" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@+id/keyfile_checkox" + app:layout_constraintEnd_toEndOf="parent" /> diff --git a/app/src/main/res/layout/item_autofill_app_id.xml b/app/src/main/res/layout/item_autofill_app_id.xml new file mode 100644 index 000000000..5358ef3dd --- /dev/null +++ b/app/src/main/res/layout/item_autofill_app_id.xml @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_autofill_service.xml b/app/src/main/res/layout/item_autofill_entry.xml similarity index 84% rename from app/src/main/res/layout/item_autofill_service.xml rename to app/src/main/res/layout/item_autofill_entry.xml index bf8cf1632..a7202488b 100644 --- a/app/src/main/res/layout/item_autofill_service.xml +++ b/app/src/main/res/layout/item_autofill_entry.xml @@ -22,7 +22,7 @@ android:orientation="horizontal"> + android:contentDescription="@string/content_description_entry_icon" + android:src="@drawable/ic_key_white_24dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_autofill_service_unlock.xml b/app/src/main/res/layout/item_autofill_unlock.xml similarity index 88% rename from app/src/main/res/layout/item_autofill_service_unlock.xml rename to app/src/main/res/layout/item_autofill_unlock.xml index 9ca888b90..9cf3cd995 100644 --- a/app/src/main/res/layout/item_autofill_service_unlock.xml +++ b/app/src/main/res/layout/item_autofill_unlock.xml @@ -16,32 +16,27 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_autofill_unlock_app_id.xml b/app/src/main/res/layout/item_autofill_unlock_app_id.xml new file mode 100644 index 000000000..b9fbfad3b --- /dev/null +++ b/app/src/main/res/layout/item_autofill_unlock_app_id.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_autofill_unlock_web_domain.xml b/app/src/main/res/layout/item_autofill_unlock_web_domain.xml new file mode 100644 index 000000000..973baa9bf --- /dev/null +++ b/app/src/main/res/layout/item_autofill_unlock_web_domain.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_autofill_web_domain.xml b/app/src/main/res/layout/item_autofill_web_domain.xml new file mode 100644 index 000000000..4258b4607 --- /dev/null +++ b/app/src/main/res/layout/item_autofill_web_domain.xml @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_button_lock.xml b/app/src/main/res/layout/view_button_lock.xml new file mode 100644 index 000000000..9f3f66162 --- /dev/null +++ b/app/src/main/res/layout/view_button_lock.xml @@ -0,0 +1,48 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_keyfile_selection.xml b/app/src/main/res/layout/view_keyfile_selection.xml new file mode 100644 index 000000000..8221ac1b8 --- /dev/null +++ b/app/src/main/res/layout/view_keyfile_selection.xml @@ -0,0 +1,49 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/database.xml b/app/src/main/res/menu/database.xml index ac9ff54ed..d94f86817 100644 --- a/app/src/main/res/menu/database.xml +++ b/app/src/main/res/menu/database.xml @@ -19,11 +19,6 @@ --> - قابل للتعديل فتح قاعدة بيانات موجودة انشاء قاعدة بيانات - قواعد البيانات الاخيرة لا تبحثفي مدخلات النسخ الاحتياطي قيد العمل… KeePassDX يحتاج صلاحية الكتابة من اجل تعديل قاعدة البيانات. خوارزمية تشفير جميع البيانات. قاعدة بيانات غير مدعومة. - اربط بطاقة الذاكرة لإنشاء او تحميل قاعدة بيانات. بناء %1$s تم حفظ كلمة السر المشفرة قاعدة البيانات لا تمتلك كلمة سر. diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index c9639f291..8606f9aec 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -107,7 +107,6 @@ Mai Cap resultat de cerca Sense gestor per aquesta url. - Obre base de dades recent : No cerquis entrades a còpia de seguretat ni paperera Omet els grups \'Còpia de seguretat\' i paperera dels resultats de cerca Creant nova base de dades… @@ -128,7 +127,6 @@ Subratllat Versió de la base de dades no suportada. Majúscules - La teva tarja SD no està muntada al teu dispositiu. No podràs carregar ni guardar una base de dades. Versió %1$s Introdueix una contrasenya i/o un arxiu clau per desbloquejar la base de dades. diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 2bf19bfad..230ab8476 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -113,7 +113,6 @@ Nikdy Žádné výsledky hledání Pro otevření tohoto URL nainstalujte webový prohlížeč. - Nedávno otevřené databáze Neprohledávat položky v záloze Vynechat skupiny „Záloha“ a \"Koš\" z výsledků vyhledávání Vytvářím novou databázi… @@ -137,7 +136,6 @@ Nepodporovaná verze databáze. Velká písmena Verze %1$s - Připojit paměťovou kartu pro založení a otevření databáze. Databázi odemknete zadáním hesla a/nebo souboru s klíčem. \n \nNezapomeňte si po každé úpravě zazálohovat kopii svého .kdbx souboru na bezpečné místo. @@ -197,8 +195,8 @@ Přístup Varování Nepoužívejte v hesle pro databázový soubor znaky mimo znakovou sadu Latin-1 (nepoužívejte znaky s diakritikou). - Opravdu chcete ponechat databázi nechráněnou (bez hesla)? - Opravdu nechcete používat šifrovací klíč? + Pokračovat bez ochrany odemknutím heslem\? + Pokračovat bez šifrovacího klíče\? Biometrická pobídka je zařízením podporována, ale není nastavena. Otevři biometrickou pobídku k otevření databáze Šifrované heslo uloženo @@ -231,7 +229,7 @@ Nechá otevřít databázi snímáním biometrického údaje Smazat šifrovací klíče Smazat všechny šifrovací klíče související s biometrickým rozlišením - Opravdu chcete smazat všechny klíče související s biometrickým rozlišením\? + Smazat všechny šifrovací klíče související s biometrickým rozlišením\? Tuto funkci se nedaří spustit. Verze %1$s vámi používaného systému Android nevyhovuje minimální verzi %2$s. Hardware nebyl rozpoznán. @@ -280,7 +278,7 @@ Propojte své heslo a otisk prstu pro rychlé odemykání databáze. Upravit položku Přidejte ke své položce vlastní kolonky. Společná data mohou být sdílena mezi více různými kolonkami. - Vytvořte k záznamu silné heslo. + Vytvořit silné heslo Vygenerujte silné heslo pro svou položku, definujte je podle kritérií formuláře, a nezapomeňte na bezpečné heslo. Přidat vlastní kolonky Registrovat další kolonku, zadat hodnotu a volitelně ji ochránit. @@ -407,7 +405,7 @@ Nastavení zabezpečení Nastavení hlavního klíče Databáze obsahuje duplikátní UUID. - Prověřením toho dialogu opraví KeePassDX chybu (založením nového UUID pro duplikáty) a bude pokračovat. + Opravit chybu založením nového UUID pro duplikáty a pokračovat\? Databáze otevřena Kopírujte pole záznamů pomocí schránky Vašeho zařízení K snadnějšímu otevření databáze použijte pokročilé odemknutí @@ -433,24 +431,24 @@ Uložit databázi Vysypat koš Provádím příkaz… - Jste si jisti, že chcete natrvalo smazat vybrané uzly\? + Natrvalo smazat vybrané uzly\? Úložiště klíčů není řádně inicializováno. Zadejte heslo než kliknete na tlačítko biometriky. Skupina Koš Uložit databázi automaticky - Automaticky uloží databázi po důležité akci (pouze v režimu \"Zápis\") + Uložit databázi po každé důležité akci (v režimu \"Zápis\") Připojené soubory Obnovit historii Smazat historii Akce auto-klíče - Automatická akce klíče Jít po stisknutí klíče Pole + Akce klíče \"Jít\" po stisknutí klíče \"Položka\" Stáhnout %1$s Zahajuji… - Probíhá: %1$d% + Probíhá: %1$d% Dokončuji… Ukončeno! Klepnout pro otevření souboru. Skrýt propadlé záznamy - Propadlé záznamy budou skryty + Propadlé záznamy jsou skryty Kontakt Příspěvky Feedback @@ -465,7 +463,14 @@ Skrýt špatné odkazy na databáze Skrýt špatné odkazy v seznamu nedávných databází Udělit právo zápisu pro uložení změn v databázi - KeePassDX © %1$d Kunzisoft je <strong>otevřený software</strong> a <strong>bey reklam</strong>. + KeePassDX © %1$d Kunzisoft je <strong>svobodný software</strong> a <strong>bey reklam</strong>. \nJe poskytován jak je, pod licencí <strong>GPLv3</strong>, bez jakékoli záruky. Abychom si <strong>udrželi svoji svobodu</strong>, <strong>opravili chyby</strong>,<strong>doplnili funkce</strong> a <strong>byli vždy aktivní</strong>, počítáme s Vaším <strong>přispěním</strong>. + Nedaří se vytvořit soubor s databází. + Přidat přílohu + Zahodit + Zahodit změny\? + Ověřit + Nastavit správu One-Time hesla (HOTP / TOTP) pro založení tokenu požadovaného pro dvoufázové ověření (2FA). + Nastavit OTP \ No newline at end of file diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 0a954c1ca..7ee233c87 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -112,7 +112,6 @@ Aldrig Ingen søgeresultater Installer en web-browser til at åbne URL. - Seneste databaser Gennemsøg ikke backup poster Udelader \"Sikkerhedskopiering\" og \"Papirkurv\" - grupper fra søgeresultater Opretter ny database… @@ -135,7 +134,6 @@ Understregning Database-versionen er ikke understøttet. Store bogstaver - Monter hukommelseskortet for at oprette eller indlæse en database. Version %1$s Angiv en adgangskode og/eller en nøglefil til at låse databasen op. \n @@ -197,7 +195,7 @@ Advarsel Undgå adgangskodetegn uden for tekstkodningsformatet i databasefilen (ukendte tegn konverteres til samme bogstav). Bekræft brug af ingen adgangskode til beskyttelse mod oplåsning\? - Bekræft ingen brug af en krypteringsnøgle? + Fortsæt uden krypteringsnøgle\? Biometrisk prompt understøttes, men er ikke konfigureret. Åbn den biometriske prompt for at låse databasen op Krypteret adgangskode er gemt @@ -230,7 +228,7 @@ Lader dig scanne din biometrisk for at åbne databasen Slet krypteringsnøgler Slet alle krypteringsnøgler, der er relateret til fingeraftryk - Bekræft sletning af alle nøgler, der er relateret til fingeraftryksgenkendelse\? + Slet alle nøgler, der er relateret til fingeraftryksgenkendelse\? Funktionen kunne ikke startes. Android-version %1$s opfylder ikke minimum versionskrav %2$s. Kunne ikke finde den tilsvarende hardware. @@ -279,7 +277,7 @@ Link adgangskoden til det scannede fingeraftryk for hurtigt at låse databasen op. Rediger posten Rediger post med brugerdefinerede felter. Pool data kan refereres mellem forskellige indtastningsfelter. - Opret en stærk adgangskode til posten. + Opret en stærk adgangskode Generer en stærk kodeord til at forbinde elementet, definer det i henhold til kriteriet for formularen og glem ikke et sikkert kodeord. Tilføj brugerdefinerede felter Registrer et ekstra felt, tilføj en værdi og beskyt det eventuelt. @@ -323,7 +321,7 @@ Magikeyboard (KeePassDX) Magikeyboard indstillinger Post - Timeout + Udløbstid Meddelelsesinfo Vis en meddelelse, når en post er til rådighed Post @@ -375,7 +373,7 @@ Kan ikke oprette database med denne adgangskode og nøglefil. Avanceret oplåsning Gem biometrisk genkendelse - ADVARSEL: Brug af biometrisk genkendelse fritager ikke for at kende hovedadgangskode. + Advarsel: hovedadgangskoden skal stadig huskes, hvis der bruges biometrisk genkendelse. Åbn database med biometrisk genkendelse Uddrag databasens legitimationsoplysninger med biometriske data Biometrisk @@ -406,7 +404,7 @@ Sikkerhedsindstillinger Indstillinger for hovednøgle Databasen indeholder dublerede UUID\'er. - Ved at godkende dialogboksen, vil KeePassDX løse problemet (ved at generere nye UUID\'er for dubletter) og fortsætte. + Løs problemet ved at generere nye UUID\'er for dubletter til at fortsætte\? Database åbnet Kopier indtastningsfelter ved hjælp af enhedens udklipsholder Brug avanceret oplåsning for at gøre det lettere at åbne en database @@ -432,12 +430,12 @@ Gem database Tøm papirkurven Udfører kommandoen… - Bekræft sletning ad de markerede noder permanent\? + Slet markerede noder permanent\? Nøglelageret er ikke korrekt initialiseret. Skriv adgangskoden, før der klikkes på den biometriske knap. Papirkurvsgruppe Gem automatisk database - Gem automatisk databasen efter en vigtig handling (kun i tilstanden \"Modificerbar\") + Gem databasen efter hver en vigtig handling (i tilstanden \"Modificerbar\") Vedhæftninger Gendan historik Slet historik @@ -445,11 +443,11 @@ Handling af Gå-tasten udføres automatisk, efter der er trykket på en Felt nøgle Hent %1$s Initialiserer… - I gang: %1$d% + I gang: %1$d% Færdiggørelse… Komplet! Tryk for at åbne filen. Skjul udløbne poster - Udløbne poster vil blive skjult + Udløbne poster er skjult Kontakt Bidrag Tilbagemelding @@ -467,4 +465,11 @@ Skjule brudte databaselinks Skjul brudte links på listen over seneste databaser Giv fil skriveadgang for at gemme databasændringer + Opsætning af engangsadgangskodestyring (HOTP / TOTP) for at generere et token anmodet om tofaktorautentisering (2FA). + Opsætning af OTP + Databasefilen kunne ikke oprettes. + Tilføj vedhæng + Kassér + Kasser ændringer\? + Valider \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a8e1f7a95..43b92a40d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -20,8 +20,11 @@ Translations from BoDEAN Translations from Matthias Dill Translations from David Ramiro ---> - Feedback +--> + + Kontakt + Mitwirken + Rückmeldung Webseite Android-Implementierung des Passwortmanagers KeePass Akzeptieren @@ -42,12 +45,13 @@ Zwischenablage-Timeout Dauer der Speicherung in der Zwischenablage %1$s in die Zwischenablage kopieren - Datenbank-Schlüsseldatei erzeugen … + Datenbank-Schlüsseldatei erzeugen… Datenbank - Entschlüsselung der Datenbankinhalte … + Entschlüsselung der Datenbankinhalte… Als Standard-Datenbank verwenden Zahlen - KeePassDX © %1$d Kunzisoft. Alle Rechte vorbehalten. Die Nutzung der Software erfolgt auf eigene Verantwortung und ohne jegliche Garantie. Die Applikation ist frei und wird unter den Bedingungen der GNU GPL Version 3 (oder später) verbreitet und lizenziert. + KeePassDX © %1$d Kunzisoft ist <strong>quelloffen</strong> und <strong>ohne Werbung</strong>.\nDie Nutzung der Software erfolgt auf eigene Verantwortung und ohne jegliche Garantie. Die Applikation wird unter den Bedingungen der <strong>GPLv3</strong> lizenziert. + Damit wir <strong>unsere Unabhängigkeit erhalten</strong>, <strong>Fehler korrigieren</strong>, <strong>Funktionen hinzufügen</strong> und <strong>weiterhin aktiv bleiben</strong> können, zählen wir auf Ihren <strong>Beitrag</strong>. Vorhandene Datenbank öffnen Letzter Zugriff Abbrechen @@ -97,7 +101,7 @@ Länge Größe der Listenelemente Schriftgröße der Listenelemente - Datenbank wird geladen … + Datenbank wird geladen… Kleinbuchstaben Passwort verstecken Passwörter standardmäßig mit (***) maskieren @@ -118,11 +122,12 @@ Nie Keine Suchergebnisse Bitte einen Webbrowser installieren, um diese URL zu öffnen. - Zuletzt geöffnete Datenbanken Papierkorb/Sicherungen nicht durchsuchen Die Gruppen „Sicherung“ und „Papierkorb“ werden bei der Suche nicht berücksichtigt - Neue Datenbank anlegen … - Ausführen … + Schnellsuche + Beim Öffnen einer Datenbank eine Suche veranlassen + Neue Datenbank anlegen… + Ausführen… Sicherheit Schreibgeschützt KeePassDX benötigt Schreibrechte, um etwas an der Datenbank zu ändern. @@ -131,7 +136,7 @@ Start Schlüsseltransformationen Zusätzliche Schlüsseltransformationen bieten einen besseren Schutz gegen Wörterbuch- oder Brute-Force-Angriffe. Allerdings dauert dann auch das Laden und Speichern der Datenbank entsprechend länger. - Datenbank wird gespeichert … + Datenbank wird gespeichert… Leerzeichen Suchen Natürliche Sortierung @@ -144,7 +149,6 @@ Großbuchstaben Warnung Passwortzeichen in der Datenbank vermeiden, die kein Text-Encoding-Format besitzen (nicht erkannte Zeichen werden in denselben Buchstaben umgewandelt). - Die Speicher einbinden, um eine Datenbank erstellen oder laden zu können. Version %1$s Geben Sie das Passwort und/oder die Schlüsseldatei ein, um Ihre Datenbank zu entsperren. \n @@ -175,7 +179,6 @@ Bildschirmsperre Datenbank sperren, wenn der Bildschirm ausgeschaltet wird Neue Datenbank erstellen - App-Design Hauptschlüssel zuweisen Pfad Dateiname @@ -195,7 +198,6 @@ Allgemein Biometrische Abfrage öffnen, um Anmeldedaten zu speichern. Diese Datenbank hat noch kein Passwort. - App-Design, das in der App genutzt wird Verschlüsselung Schlüsselableitungsfunktion Erweiterte ASCII @@ -266,7 +268,7 @@ Verknüpft Ihr Passwort mit Ihrer gescannten Biometrie, um Ihre Datenbank schnell zu entsperren. Eintrag bearbeiten Bearbeiten Sie Ihren Eintrag mit persönlichen Feldern. Persönliche Felder können mit Querverweisen aus anderen Einträgen ergänzt werden. - Ein starkes Passwort für den Eintrag erstellen. + Ein starkes Passwort erstellen Erzeugung eines starken Passworts, das mit dem Eintrag verknüpft wird, nach Kriterien, die einfach in einem Formular festgelegt werden. Dabei an die Passwortsicherheit denken. Benutzerdefinierte Felder hinzufügen Tragen Sie ein zusätzliches Feld ein, fügen Sie einen Wert hinzu und schützen Sie es optional. @@ -281,7 +283,7 @@ Auswählen, wie Einträge und Gruppen sortiert werden. Mitmachen Mithelfen, um Stabilität und Sicherheit zu verbessern und weitere Funktionen zu ermöglichen. - Anders als viele andere Passwortmanager ist dieser <strong>werbefrei</strong>, <strong>quelloffen</strong> und unter Copyleft lizenziert. <strong>Es werden keine persönlichen Daten gesammelt</strong>, in welcher Form auch immer, egal welche Version (kostenlos oder pro) Sie verwenden. + Anders als viele andere Passwortmanager ist dieser <strong>werbefrei</strong>, <strong>quelloffen</strong> und unter <strong>Copyleft lizenziert</strong>. Es werden keine persönlichen Daten gesammelt, in welcher Form auch immer, egal welche Version (kostenlos oder pro) Sie verwenden. Mit dem Kauf der Pro-Version erhält man Zugang zu dieser <strong>visuellen Funktion</strong> und unterstützt insbesondere <strong>die Umsetzung gemeinschaftlicher Projektarbeiten.</strong> Dieser <strong>visuelle Stil</strong> wurde wegen Ihrer Großzügigkeit freigeschaltet. Um unsere Freiheit zu bewahren und immer aktiv zu bleiben, zählen wir auf Ihren <strong>Beitrag.</strong> @@ -297,8 +299,8 @@ ChaCha20 AES Argon2 - Symbolepaket - In der App verwendetes Symbolepaket + Symbolpaket + In der App verwendetes Symbolpaket Eine Gruppe kann nicht in sich selbst verschoben werden. Kopieren Verschieben @@ -344,7 +346,15 @@ Vibrierende Tastendrücke Hörbare Tastendrücke Auswahlmodus - Nicht die App beenden … + Speicherort der Datenbanken + Speicherort der Datenbanken merken + Speicherort der Schlüsseldateien + Speicherort der Schlüssel zu Datenbanken merken + Zuletzt verwendete Dateien anzeigen + Speicherort zuletzt verwendeter Datenbanken anzeigen + Defekte Datenbankverknüpfungen ausblenden + Nicht funktionierende Verknüpfungen in der Liste der zuletzt verwendeten Datenbanken ausblenden + Nicht die App beenden… Datenbank sperren, wenn auf dem Hauptbildschirm der Zurück-Button gedrückt wird Beim Schließen löschen Papierkorb @@ -410,7 +420,7 @@ Sicherheits-Einstellungen Hauptschlüssel-Einstellungen Die Datenbank enthält UUID-Duplikate. - Durch die Validierung dieses Dialogs wird KeePassDX das Problem (durch Erzeugung neuer UUIDs für Duplikate) beheben und weiter ausgeführt. + Problem lösen, indem neue UUIDs für Duplikate generiert werden und danach fortfahren\? Datenbank geöffnet Eintragsfelder mithilfe der Zwischenablage des Geräts kopieren Erweitertes Entsperren verwenden, um eine Datenbank einfacher zu öffnen. @@ -435,13 +445,13 @@ Die Datenbank konnte nicht gespeichert werden. Datenbank speichern Papierkorb leeren - Befehl ausführen … + Befehl ausführen… Sind Sie sicher, dass Sie die ausgewählten Knoten dauerhaft löschen möchten\? Der Schlüsselspeicher ist nicht richtig initialisiert. Geben Sie das Passwort ein, bevor Sie auf den Biometrie-Button klicken. Papierkorb-Gruppe Datenbank automatisch speichern - Automatisches Speichern der Datenbank nach einer wichtigen Aktion (nur im Modus \"Bearbeiten\") + Automatisches Speichern der Datenbank nach einer wichtigen Aktion (im Modus \"Bearbeiten\") Anhänge Historie wiederherstellen Historie löschen @@ -449,9 +459,28 @@ Aktion der Go-Taste, die automatisch nach dem Drücken einer Feldtaste ausgeführt wird %1$s herunterladen Initialisieren… - Fortschritt: %1$d% - Fertigstellung… + Fortschritt: %1$d% + Fertigstellen… Vollständig! Tippen Sie, um die Datei zu öffnen. Abgelaufene Einträge ausblenden Abgelaufene Einträge werden ausgeblendet + App-Design + App-Design, das in der App genutzt wird + + Hell + Dunkel + Schwarz + Klassisch Dunkel + Himmel und Meer + Roter Vulkan + Purple Pro + + Datei Schreibrechte gewähren, um Datenbankänderungen zu speichern + Einrichten einer Einmal-Passwortverwaltung (HOTP / TOTP), um ein Token zu generieren, das für die Zwei-Faktor-Authentifizierung (2FA) angefordert wird. + Einrichten von OTP + Es ist nicht möglich, eine Datenbankdatei zu erstellen. + Anhang hinzufügen + Verwerfen + Änderungen verwerfen\? + Validieren \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 83ec408c0..9dc8cec34 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -113,7 +113,6 @@ Ποτέ Δεν βρέθηκαν αποτελέσματα αναζήτησης Εγκαταστήστε ένα πρόγραμμα περιήγησης για να ανοίξετε αυτήν τη διεύθυνση URL. - Πρόσφατες βάσεις δεδομένων Να μην γίνει αναζήτηση μέσα από τις καταχωρήσεις αντιγραφών ασφαλείας Παράληψη ομάδας \"Αντίγραφο Ασφαλείας\" και \"Κάδος Ανακύκλωσης\" από τα αποτελέσματα αναζήτησης Δημιουργία νέας βάσης δεδομένων… @@ -136,7 +135,6 @@ Υπογράμμιση Μη υποστηριζόμενη έκδοση βάσης δεδομένων. Κεφαλαία - Μοντάρετε την κάρτα μνήμης για να δημιουργήσετε ή να φορτώσετε μια βάση δεδομένων. Έκδοση %1$s Καταχωρίστε τον κωδικό πρόσβασης και /ή το αρχείο-κλειδί για να ξεκλειδώσετε τη βάση δεδομένων σας. \n diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1bbe3b3e9..523a8caf1 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -105,7 +105,6 @@ Nunca Sin resultado de búsqueda Instale un navegador web para abrir este URL. - Bases de datos recientes No buscar en las entradas de respaldo Omite los grupos «Respaldo» y «Papelera de reciclaje» de los resultados de búsqueda Creando nueva base de datos… @@ -125,7 +124,6 @@ Subrayado No se admite esta versión de la base de datos. Mayúsculas - Monte la tarjeta SD para crear o cargar una base de datos. Versión %1$s Introduzca una contraseña y/o un archivo de clave para desbloquear su base de datos. diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 096f20150..16634b8b9 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -115,7 +115,6 @@ Inoiz ez Emaitzarik gabeko bilaketa URL kudatzeko euskarririk ez. - Duela gutxiko datubasea ireki : Ez bilatu segurtasun kopiaren sarreretan Kendu segurtasun kopien taldea bilaketen emaitzetatik (.kdb fitxategie dagokie bakarrik) Datubase berria sortzen… @@ -137,7 +136,6 @@ Azpimarratu Euskarririk gabeko datubase bertsioa. Maiuskulak - Une honetan zure sd txartela ez dago montatua zure dispositiboan. Ezingo duzu zure datubasea kargatu edo sortu. Bertsioa %1$s Sartu pasahitz eta / edo gako fitxategi bat zure datubasea desblokeatzeko. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index ae7125b14..b0a20ce35 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -114,7 +114,6 @@ Ei koskaan Ei hakutuloksia Tälle URL:lle ei ole käsittelijää. - Avaa viimeisin salasanatietokanta : Älä etsi varmuuskopioista eikä roskakorista Poista \'Varmuuskopiot\' ja roskakori hakutuloksista Luodaan uutta tietokantaa… @@ -136,7 +135,6 @@ Alleviivattu Ei-tuettu salasanatietokannan versio. Isot kirjaimet - SD-korttia ei löydy. Et voi ladata tai tallentaa salasanatietokantaa. Versio %1$s Syötä salasana ja/tai avaintiedosto avataksesi tietokantasi. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 10cdb1588..c44eec20f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -16,7 +16,8 @@ You should have received a copy of the GNU General Public License along with KeePassDX. If not, see . ---> +--> + Commentaires Page d’accueil Implémentation Android du gestionnaire de mots de passe KeePass @@ -49,7 +50,7 @@ Déchiffrement du contenu de la base de données… Utiliser comme base de données par défaut Chiffres - KeePassDX © %1$d Kunzisoft est <strong>libre</strong> et <strong>sans publicité</strong>. \nIl est fourni tel quel, sous la licence <strong>GPLv3</strong>, sans aucune garantie. + Key Derivation Functions Dernier accès Annuler Notes @@ -123,7 +124,6 @@ Aucun résultat Installer un navigateur Web pour ouvrir cette URL. Ouvrir une base de données existante - Bases de données récentes Ne pas rechercher dans les entrées sauvegardées Omet les groupes « Sauvegarde » et « Corbeille » des résultats de recherche Création d’une nouvelle base de données… @@ -162,9 +162,8 @@ Majuscules Alerte Éviter les caractères en dehors du format de codage de caractères du fichier de base de données (les caractères non reconnus sont convertis en une même lettre). - Monter la carte mémoire pour créer ou charger une base de données. - Ne voulez-vous vraiment aucune protection de déverrouillage par mot de passe \? - Êtes-vous sûr de ne vouloir utiliser aucune clé de chiffrement \? + Continuer sans protection de déverrouillage par mot de passe \? + Continuer sans clé de chiffrement \? Version %1$s La reconnaissance biométrique est prise en charge mais n’est pas configurée. Ouvrir l’invite biométrique pour déverrouiller la base de données @@ -198,7 +197,7 @@ Permet de numériser votre empreinte biométrique pour ouvrir la base de données Supprimer les clés de chiffrement Supprime toutes les clés de chiffrement liées à la reconnaissance biométrique - Êtes-vous sûr de vouloir supprimer toutes les clés liées à la reconnaissance biométrique \? + Supprimer toutes les clés de chiffrement liées à la reconnaissance biométrique \? Impossible de démarrer cette fonctionnalité. Votre version d’Android %1$s est inférieure à la version minimale %2$s requise. Impossible de trouver le matériel correspondant. @@ -243,7 +242,7 @@ Associe votre mot de passe à votre empreinte biométrique numérisée pour déverrouiller rapidement votre base de données. Modifier l’entrée Modifie votre entrée avec des champs personnalisés. La collection des données peut être référencée entre différents champs de l’entrée. - Créer un mot de passe fort pour votre entrée. + Créer un mot de passe fort Générez un mot de passe fort à associer avec votre entrée, définissez-le facilement en fonction des critères du formulaire et n’oubliez pas de sécuriser votre mot de passe. Ajoutez des champs personnalisés Enregistrez un champ additionnel, ajoutez une valeur et facultativement le protéger. @@ -416,7 +415,7 @@ Paramètres de sécurité Paramètres de la clé maîtresse La base de données contient des doublons d’UUID. - En validant cette boîte de dialogue, KeePassDX corrigera le problème (en générant de nouveaux UUID pour les doublons) et continuera. + Résoudre le problème en générant de nouveaux UUID pour les doublons et continuer \? Base de données ouverte Copie les champs d’une entrée à l’aide du presse-papier de votre appareil Utilise le déverrouillage avancé pour ouvrir plus facilement une base de données @@ -442,24 +441,24 @@ Enregistrer la base de données Vider la corbeille Exécution de la commande… - Êtes-vous sûr de vouloir supprimer définitivement les nœuds sélectionnés \? + Supprimer définitivement les nœuds sélectionnés \? Le magasin de clés n’est pas correctement initialisé. Saisissez le mot de passe avant de cliquer sur le bouton biométrique. Groupe de la corbeille Enregistrement automatique de la base de données - Enregistre automatiquement la base de données après une action importante (uniquement en mode « Modifiable ») + Enregistre la base de données après chaque action importante (en mode « Modifiable ») Attachements Restaurer l\'historique Effacer l\'historique Action de touche auto - Action de la touche Go effectuée automatiquement après avoir appuyé sur une touche de champ + Action de la touche « Go » après avoir appuyé sur une touche « Champ » Téléchargement %1$s Initialisation… - En cours : %1$d% + En cours : %1$d% Finalisation… Terminé ! Appuyer pour ouvrir le fichier. Masquer les entrées expirées - Les entrées expirées seront masquées + Les entrées expirées sont cachées Contact Contribution Afin de <strong>garder notre liberté</strong>, <strong>corriger les bugs</strong>, <strong>ajouter des fonctionnalités</strong> et <strong>être toujours actif</strong>, nous comptons sur votre <strong>contribution</strong>. @@ -474,4 +473,11 @@ Masquer les liens rompus de base de données Masquer les liens rompus dans la liste des bases de données récentes Accorder un accès en écriture au fichier pour enregistrer les modifications de la base de données + Définir le mot de passe à usage unique (temporel ou évènementiel) pour générer un jeton demandé par l’authentification à deux facteurs (A2F). + Définir l’OTP + Impossible de créer le fichier de base de données. + Ajouter une pièce jointe + Abandonner + Abandonner les modifications \? + Valider \ 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 9c04f8c06..c499052c9 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -132,7 +132,6 @@ Instalirajte web preglednik da bi ste otvorili ovaj URL. Otvori postojeću bazu podataka Kreiraj novu bazu podataka - Nedavne baze podataka Ne pretražuj kopije unosa Kreiranje nove baze podataka… Zaštita diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 6506d893c..62cff6555 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -114,7 +114,6 @@ Soha Nincs találat Telepítsen egy webböngészőt az URL megnyitásához. - Korábbi adatbázisok Ne keressen a biztonsági mentésekben A „Biztonsági mentés” csoport kihagyása a keresésből (csak a .kdb fájlokra érvényes) Új adatbázis létrehozása… @@ -140,7 +139,6 @@ Nagybetűs Figyelmeztetés Kerülje a Latin-1 karakterkészlettől eltérő jelszókaraktereket a .kbd fájlokban, mert ugyanarra a betűre lesznek átalakítva. - Csatolja az SD kártyát, hogy adatbázist hozzon létre vagy töltsön be. Verzió: %1$s Az ujjlenyomat-leolvasás támogatott, de nincs beállítva. Ujjlenyomat-leolvasás diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index ca162efa4..939f0c4d1 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -44,7 +44,7 @@ Decodifica contenuto database… Usa come database predefinito Numeri - KeePassDX © %1$d Kunzisoft è un programma <strong>open source</strong> e <strong>senza pubblicità</strong>. + KeePassDX © %1$d Kunzisoft è un programma <strong>libero</strong> e <strong>senza pubblicità</strong>. \nViene distribuito sotto le condizioni della licenza <strong>GPL versione 3</strong> o successiva, senza alcuna garanzia. Note Apri un database esistente @@ -78,13 +78,13 @@ File non trovato. Prova a riaprirlo dal tuo gestore di file. Gestore file Genera password - conferma password - password generata + Conferma password + Password generata Nome gruppo File chiave - lunghezza + Lunghezza Password - password + Password Installa dal Play Store Installa dal F-Droid Non è possibile leggere le credenziali. Se questo dovesse riaccadere, il file del databese potrebbe essere corrotto. @@ -115,7 +115,6 @@ Mai Nessun risultato di ricerca Installa un browser web per aprire questo URL. - Database recenti Non cercare nelle voci di backup Ometti i gruppi \"Backup\" e \"Cestino\" dai risultati di ricerca Creazione nuovo database… @@ -139,12 +138,11 @@ Maiuscole Attenzione Evita password con caratteri al di fuori del formato di codifica del testo nel file di database (i caratteri non riconosciuti vengono convertiti nella stessa lettera). - Monta la scheda di memoria per poter creare o caricare un database. Versione %1$s La scansione di impronte è supportata ma non impostata. Scansione impronte Password criptata salvata - Lettura dell\'impronta fallita. Ripristina la tua password. + Impossibile leggere la chiave biometrica. Eliminala e ripeti la procedura di riconoscimento. Problema impronta: %1$s Usa l\'impronta per salvare questa password Questo database non ha ancora alcuna password. @@ -200,8 +198,8 @@ Creazione Modifica Accesso - Vuoi veramente che non ci sia una password di sblocco\? - Sei sicuro di non volere usare una chiave di cifratura? + Continuare senza aver impostato una password di sblocco \? + Continuare senza una chiave di cifratura\? Impronta non riconosciuta Cronologia Aspetto @@ -223,10 +221,10 @@ Blocca il database quando lo schermo è spento Impronta digitale Scansione di impronte - Consente la scansione di impronte per aprire il database + Consente la scansione biometrica per aprire il database Elimina chiavi di cifratura Elimina tutte le chiavi di cifratura relative al riconoscimento dell\'impronta - Sei sicuro di volere eliminare tutte le chiavi relative alle impronte? + Eliminare tutte le chiavi di cifrature associate al riconoscimento biometrico \? Impossibile avviare questa funzione. La tua versione di Android %1$s non è la minima %2$s richiesta. L\'hardware relativo non è stato trovato. @@ -236,7 +234,7 @@ Crea un nuovo database Percorso file Visualizza il percorso file completo - Usa il cestino + Uso del Cestino Sposta i gruppi e le voci nel gruppo \"Cestino\" prima di eliminarlo Carattere campi Cambia il carattere usato nei campi per una migliore visibilità @@ -405,6 +403,51 @@ Salva il database Svuota il cestino Esecuzione del comando… - Sei sicuro di voler eliminare definitivamente i nodi selezionati\? + Vuoi eliminare definitivamente i nodi selezionati\? Allegati + Richiedi una ricerca quando un database viene aperto + Ricerca rapida + Cancella cronologia + Ripristina cronologia + Per poter <strong>mantenere la nostra libertà</strong>, <strong>risolvere bug</strong>, <strong>aggiungere funzionalità</strong> ed <strong>essere sempre attivi</strong>, facciamo affidamento sul tuo <strong>contributo</strong>. + Contatto + Apri il database con il riconoscimento biometrico + Salva il riconoscimento biometrico + Il keystore non è inizializzato correttamente. + Impostazioni della chiave principale + Chiave principale + Contribuisci + Attenzione: Devi comunque ricordarti la password principale anche se usi il riconoscimento biometrico. + Garantisci il permesso di scrittura per salvare i cambiamenti del database + Nascondi link corrotti nella lista dei database recenti + Nascondi i link di database corrotti + Mostra le posizioni dei database recenti + Mostra file recenti + Salva la posizione dei keyfile + Ricorda la posizione dei database + Salva posizione dei database + Per continuare, risolvi il problema generando nuovi UUIDs per i duplicati \? + Impossibile creare il file del database. + Aggiungi allegato + Scarta + Scartare i cambiamenti\? + Convalida + Dimensione massima + Numero massimo + Apri automaticamente prompt biometrico + Limita la dimensione (in byte) della cronologia per voce + Limita il numero di elementi della cronologia per voce + Gruppo cestino + La compressione dei dati riduce le dimensioni del database. + Compressione dati + Apri automaticamente il prompt biometrico quando viene definita una chiave biometrica per un database + Utilizza lo sblocco avanzato per aprire il database più facilmente + Copia i campi di immissione utilizzando gli appunti del tuo dispositivo + Database aperto + Biometrico + Digitare la password prima di fare clic sul pulsante biometrico. + Estrai le credenziali del database con dati biometrici + Forza rinnovo + Consigliato cambiare la chiave principale (giorni) + Rinnovo raccomandato \ No newline at end of file diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 9b39fdc71..fdeff0b92 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -111,7 +111,6 @@ אף פעם אין תוצאות חיפוש אין מטפל לכתובת url זו. - פתח מסד נתונים אחרון : אל תחפש ערכי גיבוי הורד את קבוצת \"גיבוי\" מתוצאות חיפוש (תואם רק לקבצי kdb) צור מסד נתונים חדש… @@ -130,7 +129,6 @@ קו תחתון גרסת מסד נתונים לא נתמכת. רישית - כרטיס ה-SD במצב לקריאה בלבד. אתה לא תוכל לשמור או ליצור במסד הנתונים. גרסה %1$s הזן סיסמה ו/או קובץ מפתח כדי לפתוח את מסד הנתונים. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 2e038403c..7e0b057ac 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -103,7 +103,6 @@ 期限なし 検索結果に該当するものはありません。 このURLを処理できません。 - 以前使用したデータベースを開く: 検索対象から除外 \"バックアップ\"と\"ごみ箱\"を検索対象から除外します データベースファイルを作成中… @@ -123,7 +122,6 @@ アンダーライン (_) このバージョンのデータベースはサポートされていません。 英数大文字 - SDカードがマウントされていません。データベースの読込や作成ができません。 バージョン %1$s データベースに設定したパスワードを入力するかキーファイルを選択してください。 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 13e7c2b78..f58fa9da4 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -137,7 +137,6 @@ 이 URL을 열기 위해 웹 브라우저를 설치하십시오. 가지고 있는 데이터베이스 열기 새 데이터베이스 생성 - 최근 데이터베이스 백업 항목 검색하지 않기 새 데이터베이스 생성 중… 작업 중… diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index f444b4fc4..f7ef3c293 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -86,7 +86,6 @@ Mažosios raidės Minusas Pabraukimas - Atidaryti naujausią duomenų bazę Naudoti šią duomenų bazę kaip numatytąją Slėpti slaptažodžius pagal nutylėjimą Neteisingas algoritmas. diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index c20b85eb8..73297ba36 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -94,7 +94,6 @@ Nekad Nav meklēšanas rezultātu Neizdevās atvērt saiti. - Atvērt pēdējo datu bāzi : Nemeklēt kopijās un atkritnē Izlaist kopijas un atkritni no meklēšanas rezultātiem Izveido jaunu datu bāzi… @@ -116,7 +115,6 @@ Pasvītrojums Neatbalstīta datu bāzes versija. Lielie burti - Nav SD kartes. Darbības ar datu bāzi nav iespējamas. Versija %1$s Ievadiet paroli/atslēgas failu, lai atbloķētu savu datu bāzi. diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index c32746f85..f85f092a0 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -16,7 +16,8 @@ You should have received a copy of the GNU General Public License along with KeePassDX. If not, see . ---> +--> + Tilbakemelding Hjemmeside Android-implementasjon av KeePass-passordsbehandleren @@ -48,7 +49,8 @@ Dekrypterer databaseinnhold… Bruk dette som forvalgt database Siffer - KeePassDX © %1$d Kunzisoft kommer uten noen form for garanti. Dette er fri programvare, og du er velkommen til å redistribuere det i henhold til vilkårene i GPL versjon 3 eller senere. + KeePassDX © %1$d Kunzisoft er <strong>fri programvare</strong>, og <strong>reklamefritt</strong>. +\nDet tilbys som det er, i hendhold til <strong>GPL versjon 3 eller senere</strong>, uten noen garantier. Brukt Avbryt Kommentarer @@ -135,7 +137,6 @@ Ingen søkeresultater Kan ikke håndtere denne nettadressen. Velg en eksisterende database - Nylige databaser Ikke søk gjennom sikkerhetskopioppføringer Utelat \"Sikkerhetskopi\"-gruppen fra søkeresultater (har kunn innvirkning på .kdb-filer) Oppretter ny database… @@ -174,7 +175,6 @@ Store bokstaver Advarsel Unngå passordtegn utenfor tekstkodingsformat i databasefil (ukjente tegn blir konvertert til samme bokstav). - SD-kortet ditt er ikke montert på enheten din. Du vil ikke kunne laste inne eller opprette din database. Ønsker du virkelig å bruke en tom streng som ditt passord? Er du sikker på at du ønsker å bruke en krypteringsnøkkel? Versjon %1$s @@ -382,8 +382,8 @@ Minst én identitetsdetalj må angis. Du kan ikke kopiere en gruppe hit. Hemmelig nøkkel må være i Base32-format. - "Symbolet må inneholde %1$d til %2$d siffer." - %1$d med samme UUID %2$s finnes allerede. + Symbolet må inneholde %1$d til %2$d siffer. + %1$s med samme UUID %2$s finnes allerede. Oppretter database… Sikkerhetsinnstillinger Hovednøkkelinnstillinger @@ -408,4 +408,8 @@ Fullfører… Fullført. Trykk for å åpne filen. Skjul utløpte oppføringer + Hurtigsøk + Legg til vedlegg + Bidrag + Kontakt \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index ebac28b63..a9c528b6a 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -105,7 +105,6 @@ Nooit Geen zoekresultaten Installeer een webbrowser om deze URL te openen. - Recente databanken Back-upitems niet doorzoeken Hiermee worden groepen \"Back-up\" en \"Prullenbak\" uit de zoekresultaten weggelaten Bezig met creëren van nieuwe databank… @@ -125,7 +124,6 @@ Onderstrepen Niet-ondersteunde databankversie. Hoofdletters - Koppel de SD-kaart aan om een databank te creëren of laden. Versie %1$s Geef het wachtwoord en/of sleutelbestand op om je databank te ontgrendelen. \n @@ -278,7 +276,7 @@ Voeg items toe om je digitale identiteiten te beheren. \n \nVoeg groepen toe (groepen zijn gelijk aan mappen) om items in je databank te organiseren. - "Doorzoek al je items" + Doorzoek al je items Doorzoek items op titel, gebruikersnaam of andere velden om wachtwoorden te vinden. Ontgrendel de databank met je vingerafdruk Koppel je wachtwoord en vingerafdruk om de databank snel te ontgrendelen. diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index 5159725ee..6fc7ee191 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -104,7 +104,6 @@ Aldri Ingen søkjeresultat Ingen behandlar for denne adressa. - Opna nyleg brukt database : Søk ikkje i kopipostane eller søppelbøtta Søkjeresultatet inneheld ikkje oppføringar frå \'Backup\' eller søppelbøtta Lager ny database … @@ -124,7 +123,6 @@ Understreking Kan ikkje bruka databaseutgåva. Store bokstavar - SD-kortet er ikkje montert i eininga di. Du kan verken henta eller laga databasen din. Utgåve %1$s Skriv inn passordet og/eller nøkkelfil for å låsa opp databasen. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f416fdd2f..89308c663 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -102,7 +102,6 @@ Nigdy Brak wyników wyszukiwania Zainstaluj przeglądarkę internetową, aby otworzyć ten adres URL. - Ostatnio używana baza danych Nie wyszukuj wpisów kopii zapasowej Pomija grupy \"Kopia zapasowa\" i \"Kosz\" z wyników wyszukiwania Tworzenie nowej bazy danych… @@ -122,7 +121,6 @@ Podkreślenie Nieobsługiwana wersja bazy danych. Wielkie litery - Zainstaluj kartę pamięci, aby utworzyć lub załadować bazę danych. prowadź hasło i/lub plik klucza, aby odblokować bazę danych. \n \nUtwórz kopię zapasową pliku bazy danych w bezpiecznym miejscu po każdej zmianie. @@ -152,8 +150,9 @@ Niektóre urządzenia nie pozwalają aplikacjom korzystać ze schowka. Nie można wyczyścić schowka Przesuń, by wyczyścić schowek - KeePassDX © %1 $d Kunzisoft jest <strong>open source</strong> i <strong>bez reklam</strong>. + KeePassDX © %1 $d Kunzisoft jest <strong>wolnym programem</strong> i <strong>bez reklam</strong>. \nJest on dostarczany w stanie, zgodnie z licencją <strong>GPLv3</strong> bez żadnych gwarancji. +>>>>>>> translations Nie znaleziono danych wejściowych. Nie można załadować bazy danych. Nie można załadować klucza. Spróbuj zmniejszyć użycie pamięć KDF. @@ -196,8 +195,8 @@ Unikaj znaków hasła spoza formatu kodowania tekstu w pliku bazy danych (nierozpoznane znaki są konwertowane na tę samą literę). Kosz na dole Tytuł - Czy naprawdę nie chcesz ochrony przed odblokowaniem hasła\? - Czy na pewno nie chcesz używać żadnego klucza szyfrowania? + Kontynuować bez ochrony odblokowującej hasło\? + Kontynuować bez klucza szyfrowania\? Wersja %1$s Skanowanie odcisków palców jest obsługiwane, ale nie skonfigurowane. Zapisano zaszyfrowane hasło @@ -232,7 +231,7 @@ Umożliwia zeskanowanie danych biometrycznych w celu otwarcia bazy danych Usuń klucze szyfrowania Usuń wszystkie klucze szyfrowania związane z rozpoznawaniem linii papilarnych - Czy na pewno chcesz usunąć wszystkie klucze związane z rozpoznawaniem linii papilarnych\? + Czy usunąć wszystkie klucze szyfrowania związane z rozpoznawaniem biometrycznym\? Nie można uruchomić tej funkcji. Twoja wersja Androida %1$s nie spełnia wymaganej minimalnej wersji %2$s. Nie można znaleźć odpowiedniego sprzętu. @@ -281,7 +280,7 @@ Połącz swoje hasło z zeskanowanym odciskiem palca, aby szybko odblokować bazę danych. Edytuj wpis Edytuj swój wpis za pomocą pól niestandardowych. Dane puli mogą być przywoływane między różnymi polami wprowadzania. - Utwórz silne hasło do swojego wpisu. + Utwórz silne hasło Wygeneruj silne hasło, które będzie kojarzyć się z Twoim wpisem, łatwo zdefiniuj je zgodnie z kryteriami formularza i nie zapomnij o bezpiecznym haśle. Dodaj niestandardowe pola Zarejestruj dodatkowe pole, dodaj wartość i opcjonalnie chroń je. @@ -420,7 +419,7 @@ Okres musi wynosić od %1$d do %2$d sekund. Token musi zawierać cyfry od %1$d do %2$d. %1$s o tym samym identyfikatorze UUID %2$s już istnieje. - Weryfikując to okno dialogowe, KeePassDX rozwiąże problem (poprzez wygenerowanie nowych UUID dla duplikatów) i będzie kontynuował. + Rozwiązać problem poprzez stworzenie nowych identyfikatory UUID duplikatów, aby kontynuować\? Skopiuj pola wprowadzania danych za pomocą schowka urządzenia Użyj zaawansowanego odblokowywania w celu łatwiejszego otwierania bazy danych Kompresja danych zmniejsza rozmiar bazy danych. @@ -433,24 +432,24 @@ Zapisz bazę danych Opróżnij kosz Wykonywanie polecenia… - Czy na pewno chcesz trwale usunąć wybrane węzły\? + Czy trwale usunąć wybrane węzły\? Magazyn kluczy nie został poprawnie zainicjowany. Wpisz hasło przed kliknięciem przycisku biometrycznego. Kosz grupy Automatycznie zapisuj bazę danych - Automatycznie zapisz bazę danych po ważnym działaniu (tylko w trybie „Modyfikowalnym”) + Zapisz bazę danych po każdym ważnym działaniu (w trybie „Modyfikowalnym”) Załączniki Przywróć historię Usuń historię Automatyczne działanie klucza - Działanie klawisza Go wykonywane jest automatycznie po naciśnięciu klawisza Field + Akcja klawisza „Idź” po naciśnięciu klawisza „Pole” Pobierz %1$s Inicjowanie… - W trakcie realizacji: %1$d% + W trakcie realizacji: %1$d% Kończę… Kompletny! Stuknij, aby otworzyć plik. Ukryj wygasłe wpisy - Wygasłe wpisy zostaną ukryte + Wygasłe wpisy są ukryte Kontakt Aby <strong>zachować naszą wolność</strong>, <strong>sprawdzać błędy</strong>, <strong>dodać funkcje</strong> i <strong>by być zawsze aktywnym</strong>, liczymy na twój <strong>wkład</strong>. Szybkie wyszukiwanie @@ -465,4 +464,11 @@ Ukryj uszkodzone łącza na liście najnowszych baz danych Przyznaj dostęp do zapisu pliku, aby zapisać zmiany w bazie danych Wkład + Skonfiguruj zarządzanie hasłem jednorazowym (HOTP / TOTP), aby wygenerować token wymagany do uwierzytelniania dwuskładnikowego (2FA). + Konfiguracja OTP + Nie można utworzyć pliku bazy danych. + Dodaj załącznik + Odrzuć + Odrzucić zmiany\? + Walidacja \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index cd8717d45..138e29446 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -103,7 +103,6 @@ Nunca Sem resultados na busca Instale um navegador para abrir esta URL. - Bancos de dados recentes Não procurar por entradas no backup ou na lixeira Omite os grupos \"Backup\" e \"Lixeira\" dos resultados da busca Criando novo banco de dados… @@ -123,7 +122,6 @@ Sublinhado Versão de banco de dados não suportada. Letras maiúsculas - Monte o cartão SD para criar ou abrir um banco de dados. Versão %1$s Entre com a senha e/ou com o caminho para o arquivo-chave do banco de dados. \n diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 70d18d0b1..34711588d 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -115,7 +115,6 @@ Nunca A pesquisa não obteve resultados Instale um navegador para abrir esta URL. - Bancos de dados recentes Não procurar por entradas no backup ou na lixeira Omite os grupos \"Backup\" e \"Lixeira\" dos resultados da busca A criar nova base de dados… @@ -141,7 +140,6 @@ Maiúsculas Aviso Evite caracteres fora do formato de codificação do ficheiro do banco (todos os caracteres não reconhecidos são convertidos para a mesma letra). - Monte o cartão SD para criar ou abrir um banco de dados. Versão %1$s Entre com a palavra-passe e/ou com o caminho para o ficheiro-chave do banco de dados. \n diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index a29062d8e..55fac8a63 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -43,7 +43,7 @@ Actualizați Elimina Câmpuri închise - %1$s is either \\\"Nume\\\" si \\\"Parola\\\". + Selectați pentru a copia %1$s în clipboard Obtinerea cheii bazei de date… Baza de date Decriptarea continutului bazei de date.… @@ -167,16 +167,15 @@ Minus Niciodata Nu există Rezultate - KeePassDX © %1$d Kunzisoft is <strong>sursa deschisa<strong> and <strong>fara publicitate<strong>. -\nEste prevăzut așa cum este, sub <strong>GPLv3<strong> licenta,fara nici un fel de garantie. - In oridine sa <strong>pastram libertatea noastra <strong>, <strong>fix bugs<strong>, <strong>adăugați funcții<strong> si<strong>sa fie intotdeauna activ<strong>", ne bazam pe "<strong>contributie.<strong> + KeePassDX © %1$d Kunzisoft is <strong>sursa deschisa<strong> and <strong>fara publicitate</strong>. +\nEste prevăzut așa cum este, sub <strong>GPLv3</strong> licenta, fara nici un fel de garantie. + In oridine sa <strong>pastram libertatea noastra</strong>, <strong>fix bugs</strong>, <strong>adăugați funcții</strong> si<strong>sa fie intotdeauna activ<strong>, ne bazam pe <strong>contributie</strong>. Ascundeți parolele Mascați parolele (***) în mod implicit Despre Instalați un browser web pentru a deschide această adresă URL. Deschide baza de date existentă Creați o bază de date nouă - Baze de date recente Crearea noii baze de date … Lucrând … Protecție @@ -217,7 +216,6 @@ Cu majuscule Avertizare Evitați caracterele parole în afara formatului de codare a textului în fișierul bazei de date (caracterele nerecunoscute sunt convertite în aceeași literă). - Montați cardul de memorie pentru a crea sau încărca o bază de date. Chiar nu doriți nicio protecție de deblocare a parolei\? Ești sigur că nu vrei să folosești nicio cheie de criptare\? Sigur doriți să ștergeți definitiv nodurile selectate\? @@ -394,7 +392,7 @@ Cumpărând versiunea pro, veți avea acces la acest <strong> stil vizual </strong> și vă va ajuta în special <strong> implementarea proiectelor comunitare. </strong> Acest <strong> stil vizual </strong> este disponibil datorită generozității tale. Pentru a ne păstra libertatea și pentru a fi mereu activi, ne bazăm pe contribuția dvs. <strong>. </strong> - Această caracteristică este <strong> în curs de dezvoltare </strong> și necesită ca contribuția dvs <strong> să fie disponibilă în curând. + 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>, încurajezi dezvoltatorii să creeze <strong> funcții noi </strong> și să <strong> remedieze erori </strong> în conformitate cu observațiile tale. @@ -405,7 +403,7 @@ Contribuie Descărcați %1$s Inițializare … - In progress: %1$d% + In progress: %1$d% Finalizare … Complet! Atingeți pentru a deschide fișierul. Rijndael (AES) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 41870ed89..d1fec7600 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -43,7 +43,7 @@ Расшифровка базы… База по умолчанию Цифры - KeePassDX © %1$d Kunzisoft с <strong>открытым исходным кодом</strong> и <strong>без рекламы</strong>. + KeePassDX © %1$d Kunzisoft — это <strong>свободное программное обеспечение</strong> и <strong>без рекламы</strong>. \nРаспространяется под лицензией <strong>GPLv3</strong> без каких-либо гарантий. Открыть существующую базу Доступ @@ -115,7 +115,6 @@ Никогда Совпадения не найдены Установите браузер, чтобы открыть этот URL. - Недавно открытые базы Не искать в резервных копиях Не искать в группах \"Резервирование\" и \"Корзина\" Создание новой базы… @@ -141,7 +140,6 @@ ЗАГЛАВНЫЕ Внимание Избегайте использования в пароле символов вне кодировки текста в файле базы, так как эти символы будут преобразованы в одинаковый символ. - Подключите хранилище для создания или загрузки базы. Версия %1$s Биометрия поддерживается, но не настроена. Ожидание биометрического ключа для разблокировки базы @@ -205,8 +203,8 @@ Сначала группы \"Корзина\" внизу Название записи - Вы действительно не хотите использовать пароль для защиты базы\? - Вы действительно не хотите использовать ключ шифрования? + Продолжить без пароля для защиты базы\? + Продолжить без ключа шифрования\? Биометрический ключ не распознан История Внешний вид @@ -232,7 +230,7 @@ Включить разблокировку базы при помощи биометрического ключа Удалить ключи шифрования Удалить все ключи шифрования, связанные с распознаванием биометрического ключа - Вы уверены, что хотите удалить все ключи, связанные с биометрическим ключом\? + Удалить все ключи шифрования, связанные с биометрическим распознаванием\? Невозможно запустить эту функцию. Ваша версия Android %1$s ниже минимально необходимой %2$s. Соответствующее оборудование не найдено. @@ -281,7 +279,7 @@ Ищите записи по названию, имени или другим полям для быстрого доступа к своим паролям. Редактируйте записи Редактируйте записи с настраиваемыми полями. Возможны перекрёстные ссылки между полями разных записей. - Создайте надёжный пароль для записи. + Создайте надёжный пароль Создайте надёжный пароль, связанный с записью, легко настраиваемый под критерии формы. И не забудьте главный пароль от базы. Добавляйте настраиваемые поля Зарегистрируйте дополнительное поле, добавьте значение и при необходимости защитите его. @@ -388,14 +386,14 @@ Безопасность История Настройка одноразового пароля - Тип одноразового пароля + Тип OTP Секретный ключ Время (в секундах) Счётчик Цифры Алгоритм - Одноразовый пароль - Недействительный секретный ключ одноразового пароля. + OTP + Некорректный OTP. Должен быть установлен, по крайней мере, один пароль. Вы не можете копировать группу сюда. Секретный ключ должен быть в формате BASE32. @@ -407,7 +405,7 @@ Настройки безопасности Настройки главного пароля База содержит повторяющиеся UUID. - Если вы разрешите, KeePassDX исправит проблему (путём создания новых UUID для дубликатов) и продолжит работу. + Исправить проблему путём создания новых UUID для дубликатов и продолжить работу\? База открыта Копирование полей ввода с помощью буфера обмена устройства Использовать дополнительную разблокировку для более лёгкого открытия базы данных @@ -433,20 +431,20 @@ Сохранить базу Очистить \"корзину\" Выполнение команды… - Вы уверены, что хотите навсегда удалить выбранные узлы\? + Безвозвратно удалить выбранные узлы\? Хранилище ключей не инициализировано должным образом. Введите пароль перед нажатием кнопки биометрии. Группа \"корзины\" Автосохранение базы - Автоматическое сохранение базы после каждого важного действия (только в \"режиме записи\") + Сохранять базу после каждого важного действия (в \"режиме записи\") Вложения Восстановить историю Удалить историю Автоматическое действие кнопки - Выполнять команду \"Ввод\" автоматически после нажатия кнопки заполнения поля + Выполнять команду \"Ввод\" после нажатия кнопки заполнения поля Скачать %1$s Инициализация… - Выполнение: %1$d% + Выполнение: %1$d% Завершение… Готово! Нажмите, чтобы открыть файл. Скрывать устаревшие записи @@ -465,4 +463,11 @@ Скрывать отсутствующие Не показывать неработающие ссылки в списке последних открытых баз Необходимо разрешение на запись в файл для сохранения изменений базы + Проверить + Отменить изменения\? + Отменить + Добавить вложение + Невозможно создать файл базы. + Настройте OTP + Настройте управление одноразовыми паролями (HOTP / TOTP) для создания токена, запрашиваемого при двухфакторной аутентификации (2FA). \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index e4e1eeaff..c97bb5836 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -103,7 +103,6 @@ Nikdy Žiadne výsledky hľadania Žiaden manažér pre url. - Otvoriť poslednú databázu : Neprehľadávať položky Vynechať skupinu \'Backup\' a Recycle Bin z výsledkov hľadania Vytváram novú databázu… @@ -123,7 +122,6 @@ Podčiarknuté Nepodporovaná verzia databázy. Veľké písmená - Vaša SD karta nie je momentálne pripojená k zariadeniu. Nemôžete načítať, alebo vytvoriť databázu. Version %1$s Vložte heslo a / alebo keyfile pre odomknutie databázy. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 7c1866ac7..cc17ce5c5 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -114,7 +114,6 @@ Aldrig Inget sökresultat Installera en webbläsare för att öppna denna URL. - Senast öppnade databaser Sök inte efter backup-poster Utelämnar poster i grupperna \"Backup\" och \"Papperskorg\" Skapar ny databas… @@ -136,7 +135,6 @@ Understreck Databasversionen stöds ej. Versaler - Montera SD-kortet för att skapa eller ladda in en databas. Version %1$s Ange lösenord och/eller nyckelfil för att öppna databasen. \n diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 1da29f955..93fbfd0d6 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -16,7 +16,8 @@ You should have received a copy of the GNU General Public License along with KeePassDX. If not, see . ---> +--> + Geri Bildirim Ana sayfa KeePass parola yöneticisinin Android uygulaması @@ -64,7 +65,8 @@ Veritabanı içeriği deşifre ediliyor… Varsayılan veritabanı olarak kullan Rakamlar - KeePassDX © %1$d Kunzisoft kesinlikle bir garanti vermez. Bu, libre yazılımıdır ve GPL sürüm 3 veya üzeri şartlar altında yeniden dağıtmanız mümkündür. + KeePassDX © %1$d Kunzisoft <strong>özgür yazılımdır</strong> ve <strong>reklam içermez</strong>. +\n<strong>GPLv3</strong> lisansı altında sağlanmaktadır, herhangi bir garanti vermez. Süre sonu Anahtar dosya Arcfour akış şifresi desteklenmiyor. @@ -76,7 +78,7 @@ En az bir parola oluşturma türü seçilmelidir. Parolalar uyuşmuyor. \"Dönüşüm turları\" çok yüksek. 2147483648\'e ayarlayın. - Her dizenin bir alan adı olmalıdır. + Her dizginin bir alan adı olmalıdır. \"Uzunluk\" alanına pozitif bir tam sayı girin. Otomatik doldurma hizmeti etkinleştirilemedi. Bir grubu kendine taşıyamazsın. @@ -135,7 +137,6 @@ Bu URL\'u açmak için bir web tarayıcısı yükleyin. Mevcut veritabanını aç Yeni veritabanı oluştur - Son veritabanları Yedek girişleri arama Arama sonuçlarından \"Yedekleme\" ve \"Geri dönüşüm kutusu\" gruplarını atlar Yeni veritabanı oluştur… @@ -175,9 +176,8 @@ Büyük harf Uyarı Veritabanı dosyasındaki metin kodlama formatının dışındaki parola karakterlerinden kaçının (tanınmayan karakterler benzer harfe dönüştürülür). - Bir veritabanı oluşturmak veya yüklemek için hafıza kartını takın. - Gerçekten parolasız açma koruması mı istiyorsunuz\? - Herhangi bir şifreleme anahtarı kullanmak istemediğinize emin misiniz\? + Parola kilidi koruması olmadan devam edilsin mi\? + Şifreleme anahtarı olmadan devam edilsin mi\? Sürüm %1$s Yapı %1$s Parmak izi taraması desteklenir, ancak kurulmaz. @@ -212,7 +212,7 @@ Veritabanını açmak için biyometriklerinizi taramanızı sağlar Şifreleme anahtarlarını silin Parmak izi tanıma ile ilgili tüm şifreleme anahtarlarını silin - Parmak izi tanıma ile ilgili tüm tuşları silmek istediğinizden emin misiniz\? + Biyometrik tanıma ile ilgili tüm şifreleme anahtarları silinsin mi\? Bu özellik başlatılamadı. Android sürümünüz %1$s, gerekli minimum %2$s sürümünü karşılamıyor. İlgili donanım bulunamadı. @@ -278,10 +278,10 @@ Veritabanınızı hızlıca açmak için parolanızı taranan parmak izinize bağlayın. Girdiyi düzenle Girdinizi özel alanlarla düzenleyin. Havuz verileri farklı giriş alanları arasında referans alınabilir. - Girdiniz için güçlü bir parola oluşturun. + Güçlü bir parola oluşturun Girişinizle ilişkilendirmek için güçlü bir şifre oluşturun, formun kriterlerine göre kolayca tanımlayın ve güvenli şifreyi unutmayın. Özel alanlar ekle - Ayrıca koruyabileceğiniz yeni bir formu doldurarak temel bir tedarik edilmemiş alanı kaydedin. + Ek bir alan kaydedin, bir değer ekleyin ve isteğe bağlı olarak koruyun. Veritabanınızın kilidini açın Veritabanınızın kilidini açmak için parola ve/veya anahtar dosya girin. \n @@ -301,7 +301,7 @@ Girdilerin ve grupların nasıl sıralandığını seçin. Katıl Daha fazla özellik ekleyerek istikrarı, güvenliği artırmaya yardımcı olun. - Birçok parola yönetimi uygulamasının aksine, bu uygulama <strong>reklam içermez</strong>, <strong>açık kaynaklı</strong> ve <strong>copyleft lisanslıdır</strong>. Hangi sürümü (ücretsiz veya profesyonel) kullanırsanız kullanın, herhangi bir biçimde <strong>kişisel veri toplanmamaktadır</strong>. + Birçok parola yönetimi uygulamasının aksine, bu uygulama <strong>reklam içermez</strong>, <strong> copyleft lisanslı özgür yazılımdır</strong> ve hangi sürümü kullanırsanız kullanın, sunucularında kişisel veri toplamaz. Profesyonel sürümü satın alarak, bu <strong>görsel stile</strong> erişebilecek ve özellikle <strong>topluluk projelerinin gerçekleştirilmesine</strong> yardımcı olacaksınız. Bu <strong>görsel stil</strong>, cömertliğiniz sayesinde kullanılabilir. Özgürlüğümüzü korumak ve daima aktif olmak için <strong>katkılarınıza</strong> güveniyoruz @@ -390,7 +390,7 @@ Güvenlik ayarları Ana anahtar ayarları Veritabanı tekrarlanan UUID\'ler içermektedir. - Bu iletişim kutusunu doğrulayarak, KeePassDX sorunu çözecek (tekrarlananlar için yeni UUID\'ler oluşturarak) ve devam edecektir. + Tekrarlananlar için yeni UUID\'ler oluşturarak sorunu çöz ve devam et\? Veritabanı açıldı Cihazınızın panosunu kullanarak giriş alanlarını kopyala Veritabanını daha kolay açmak için gelişmiş kilit açma özelliğini kullan @@ -404,29 +404,29 @@ Ana anahtarın değiştirilmesini öner (gün) Yenilemeyi zorla Ana anahtarın değiştirilmesini gerektir (gün) - Bir dahaki sefere yenilemeyi zorla + Bir dahaki sefere yenilemeye zorla Bir dahaki sefere ana anahtarı değiştirmeyi gerektirir (bir kez) Varsayılan kullanıcı adı Özel veritabanı rengi Sıkıştırma Yok gzip - Cihaz klavye Ayarları + Cihaz klavye ayarları Veritabanı kaydedilemedi. Veritabanını kaydet Geri dönüşüm kutusunu boşalt Komut çalıştırılıyor… - Seçili düğümleri kalıcı olarak silmek istediğinizden emin misiniz\? + Seçilen düğümler kalıcı olarak silinsin mi\? Anahtar deposu düzgün bir şekilde başlatılmadı. Biyometrik butona tıklamadan önce şifreyi yazın. Geri dönüşüm kutusu grubu Veritabanını otomatik kaydet - Önemli bir işlemden sonra veritabanını otomatik olarak kaydet (yalnızca \"Değiştirilebilir\" modunda) + Her önemli işlemden sonra veri tabanını kaydet (\"Değiştirilebilir\" modda) Ekler Geçmişi geri yükle Geçmişi sil Otomatik tuş eylemi - Alan tuşuna bastıktan sonra otomatik olarak gerçekleştirilen Git tuşunun eylemi + \"Alan\" tuşuna bastıktan sonra \"Git\" tuşu eylemi İndir %1$s Başlatılıyor… Devam ediyor: %1$d% @@ -434,4 +434,25 @@ Tamamlandı! Dosyayı açmak için dokunun. Süresi dolmuş girdileri gizle Süresi dolmuş girdiler gizlenecek + Veritabanı değişikliklerini kaydetmek için dosya yazma erişimi ver + Son veritabanları listesindeki bozuk bağlantıları gizle + Bozuk veritabanı bağlantılarını gizle + Son veritabanlarının konumlarını göster + Son dosyaları göster + Veri tabanını anahtar dosyalarının konumunu hatırla + Anahtar dosyalarının konumlarını kaydet + Veri tabanlarının konumlarını hatırla + Veri tabanlarının konumlarını kaydet + Veri tabanını açarken arama iste + Hızlı arama + Katkı + İletişim + İki öğeli kimlik doğrulaması (2FA) için istenen bir belirteç oluşturmak için Bir Kerelik Parola yönetimini (HOTP / TOTP) ayarlayın. + OTP ayarla + Veritabanı dosyası oluşturulamıyor. + <strong>Özgürlüğümüzü korumak</strong>, <strong>hataları düzeltmek</strong>, <strong>özellikler eklemek</strong> ve <strong>her zaman etkin olmak</strong> için, <strong>desteğinize</strong> güveniyoruz. + Ek ekle + Vazgeç + Değişikliklerden vazgeç\? + Doğrula \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e860e6e8f..ea402419c 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -104,7 +104,6 @@ Ніколи Нічого не знайдено. Нема програми для опрацювання цього посилання. - Відкрити останню базу даних : Не шукати записів з резервного копіювання та кошиків Пропустити групу \'Резервна копія\' та Кошик серед результатів пошуку Створення нової бази даних… @@ -124,7 +123,6 @@ Підкреслення Непідтримувана версія бази даних. Верхній регістр - Ваша карта пам’яті зараз не змонтована на телефоні. Ви не зможете завантажити або створити базу даних. Версія %1$s Введіть пароль і/або файл ключа для відкриття бази даних. diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index 688e72ea1..14addc6ea 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -28,6 +28,7 @@ diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4adbfa755..984504f38 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 实现的密码管理器 接受 添加条目 添加群组 @@ -39,7 +39,7 @@ 正在解密数据库内容… 设为默认数据库 数字 - KeePassDX © %1$d 是Kunzisoft的一个<strong>开源</strong>和<strong>无广告</strong>软件。 + KeePassDX © %1$d 是 Kunzisoft 的一个<strong>自由软件</strong>并且<strong>不含广告</strong>。 \n它是根据<strong>GPLv3</strong>许可证分发的,您可在遵循GPL 3或者更高版本的协议下重新发布。Kunzisoft对软件的质量和性能等问题不提供任何形式的担保。 打开已有数据库 访问时间 @@ -103,7 +103,6 @@ 从不 没有搜索结果 需要安装网络浏览器才能打开这个URL。 - 最近用过的数据库 正在新建数据库… 正在处理… 移除 @@ -121,10 +120,9 @@ 下划线 不支持的数据库版本。 大写 - 请挂载内存卡以新建或加载数据库。 输入密码和/或密钥文件来解锁你的数据库。 -\n -\n记得在每次更改后将数据库文件备份至安全的地方。 +\n +\n记得在每次做出更改后,将数据库文件备份至安全的地方。 5秒 10秒 @@ -173,7 +171,7 @@ 粘贴 取消 显示密码 - 删除已保存生物识别密钥 + 删除已保存的与生物识别相关的密钥 只读 可修改 搜索时忽略备份条目 @@ -222,7 +220,7 @@ 其他 键盘 魔法键盘 - 写保护(只读模式) + 写入保护(只读模式) 默认以只读方式打开数据库 下载 贡献 @@ -235,18 +233,18 @@ 避免在数据库中保存编码格式外字符的密码(未识别的字符将转换为同一字符)。 群组在前 回收站在末尾 - 确定不用密码解锁? - 确认使用空密钥吗? + 确定不使用密码保护? + 确认不使用加密密钥吗? 版本%1$s 支持生物识别设置,但并未启用生物识别。 打开生物识别对话框以解锁数据库 加密密码已保存 - 不能读取生物识别密钥,请删除所有生物密钥,并重新录入。 + 不能读取生物识别密钥,请删除所有生物识别密钥,并重新录入。 无法识别生物识别信息 生物识别错误:%1$s 当前数据库无密码。 设为默认的填充服务 - 启用自动填充功能,以便捷地在其他程序中填写信息 + 启用自动填充功能,以快速填写其他应用中的表单 密码生成长度 设置生成密码的默认长度 密码字符集 @@ -258,8 +256,8 @@ 生物识别解锁 通过生物识别解锁数据库 删除加密密钥 - 删除所有与生物相关的加密密钥 - 要删除所有生物识别密钥吗? + 删除所有与生物识别相关的密钥 + 要删除所有与生物识别相关的密钥吗? 无法启动此功能。 你的Android版本%1$s无法满足程序对系统版本%2$s的要求。 找不到所需的硬件。 @@ -311,16 +309,16 @@ 将主密钥与生物识别信息关联,以快速解锁数据库。 编辑此条目 使用自定义字段编辑条。自定义字段可以在不同的条目间引用。 - 为记录新建强密码。 - 依据表格中的标准生成新密码,并将密码与条目关联起来,永不忘记。 + 新建一个强密码 + 依据表单中的标准进行简单的定义,随机为该条目生成一个强密码,不在担心忘记安全密码。 添加自定义字段 添加一个新的字段并添加为其添加一个值,此时可以选择是否保护该字段及其值。 解锁数据库 - 为数据库开启写保护(只读) - 在会话中改变打开模式。 -\n -\n“写保护(只读)”将阻止对数据库的任何修改。 -\n“可编辑(可写)”让添加、删除或者修改元素。 + 数据库启用写入保护(只读) + 更改会话的打开模式。 +\n +\n“写入保护(只读)”可防止对数据库的意外更改。 +\n“可编辑(可写)”允许您添加、删除或者修改元素。 复制字段 已复制的字段可以粘贴到任何地方。 \n @@ -331,21 +329,21 @@ 选择条目和群组的排序方式。 参与开发 帮助增加稳定性,安全性并添加更多的功能。 - 不同于大多数的密码管理程序,无论您是使用免费版本还是付费版本的KeePassDX,这都是一款<strong>没有广告</strong>,<strong>基于copylefted版权协议的免费软件</strong>,同样的本软件的任何版本也不会收集您的个人信息。 - 通过购买高级版本,您将解锁全部<strong>主题样式</strong>,重要的是,您会为<strong>社区项目的进行</strong>提供的帮助 - 此<strong>主题样式</strong>现在已经可用,感谢慷慨相助。 - 为继续建设此自由项目,我们需要<strong>捐助。</strong> - 这个特性目前<strong>仍在开发中</strong>,<strong>捐助</strong>将使这个特性在未来变得可用。 + 不同于大多数的密码管理程序,无论您是使用免费版本还是付费版本的 KeePassDX,这都是一款<strong>没有广告</strong>,<strong>基于 copylefted 版权协议的自由软件</strong>。同时,本软件的任何版本都不会收集您的任何个人信息。 + 通过购买高级版本,您将解锁全部<strong>主题样式</strong>,重要的是,您会为<strong>社区项目的进行</strong>提供帮助 + 此<strong>主题样式</strong>已可用,感谢您的慷慨相助。 + 为继续建设此自由项目让其保持活跃,我们需要您的<strong>捐赠。</strong> + 此功能目前<strong>仍在开发中</strong>,<strong>捐助</strong>将使该功能很快变得可用。 通过购买<strong>专业</strong>版, 通过<strong>贡献</strong>, - 您的留言,鼓励了开发人员开发<strong>新特性</strong>并<strong>修复程序缺陷</strong>。 + 您的留言,是对开发人员添加<strong>新功能</strong>及<strong>修复 bugs</strong> 的鼓励。 非常感谢您的捐助和贡献。 我们正在努力的研发并尽快发布新特性。 别忘了更新程序。 选择模式 不要终止程序… 按返回键以锁定 - 在点按根屏幕上的后退按钮时锁定数据库 + 点击屏幕底部的返回键时锁定数据库 关闭程序时清空剪贴板 清除通知时锁定数据库 回收站 @@ -397,7 +395,7 @@ 数字位数 算法 一次性密码 - 一次性密码密钥错误。 + 错误的一次性密码密钥。 至少需要设置一个凭据。 不能将群组复制到这里。 密钥必须是BASE32格式。 @@ -409,7 +407,7 @@ 安全设置 主密钥设置 数据库包含重复UUID。 - 通过验证此对话框,KeePassDX将解决这个问题(通过给重复项生成新的UUID)并继续。 + 通过为重复项生成新的UUID以继续解决问题? 数据库开启 使用设备的剪贴板来复制输入字段 使用高级解锁轻松打开数据库 @@ -423,7 +421,7 @@ 建议修改主密钥(以天为单位) 强制修改 要求修改主密钥(以天为单位) - 下次强制修改 + 下次强制更新修改 下次强制修改主密钥(一次) 默认用户名 自定义数据库颜色 @@ -435,27 +433,27 @@ 保存数据库 清空回收站 正在执行命令…… - 确认要永久删除选中的条目吗? + 是否永久删除选中的条目? 请先输入密码,再点击生物识别按钮。 回收站(组) 自动保存数据库 - 在进行重要操作后自动保存数据库(仅在编辑模式下有效) + 在每次执行重要操作后保存数据库(仅在编辑模式下有效) 密钥库未正确地初始化。 附件 恢复历史记录 删除历史记录 自动键操作 - 填入用户名或密码后直接登录 + 填充条目后直接登录 下载%1$s 正在初始化… - 进行中:%1$d% + 进行中:%1$d% 正在完成… 完成!点击打开文件。 隐藏过期条目 过期条目将被隐藏 联系我们 贡献 - 为了<strong>保持我们的自由</strong>,<strong>修复错误</strong>,<strong>添加功能</strong>和<strong>始终保持活跃</strong>,我们期待您的 <strong>贡献</strong>。 + 为了<strong>维持我们的自由</strong>、<strong>修复 bugs</strong>、<strong>添加更多功能</strong>以及<strong>始终保持项目活跃</strong>,我们期待您的<strong>贡献</strong>。 快速搜索 打开数据库时询问是否进行搜索 数据库保存的路径 @@ -465,4 +463,11 @@ 隐藏已损坏的数据库路径 在最近的数据库列表中隐藏已损坏的数据库的链接 授予软件文件读写访问权限以保存数据库更改 + 设置一次性密码管理(HOTP / TOTP)以生成请求的用于双重身份验证(2FA)的令牌。 + 设置一次性密码 + 丢弃 + 无法创建数据库文件。 + 添加附件 + 放弃更改? + 验证 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 923eb5065..e73e7a775 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -102,7 +102,6 @@ 從不 沒有搜索結果 沒有這個鏈結的處理程式。 - 最近打開的資料庫: 創建新資料庫中… 工作中… 移除 @@ -120,7 +119,6 @@ 強調 不支援的資料庫版本。 大寫 - 你的SD卡目前尚未安裝在您的設備上。你將無法讀或創建您的資料庫。 輸入密碼和/或一個密鑰檔來解鎖你的資料庫. 5秒 diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 9fb0a4825..e95e9514f 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -41,6 +41,7 @@ https://github.com/Kunzisoft/KeePassDX/wiki/Magikeyboard https://github.com/Kunzisoft/KeePassDX/wiki/Clipboard https://github.com/Kunzisoft/KeePassDX/wiki/Autofill + https://github.com/Kunzisoft/KeePassDX/wiki/File-Manager-and-Sync --,--`--,{@ @@ -77,6 +78,8 @@ true lock_database_back_root_key false + lock_database_show_button_key + true password_length_key list_password_generator_options_key hide_password_key @@ -116,6 +119,7 @@ autofill_explanation_key settings_autofill_enable_key false + settings_autofill_key keyboard_selection_entry_key false keyboard_notification_entry_key @@ -130,6 +134,8 @@ true keyboard_key_sound_key false + autofill_auto_search_key + true settings_advanced_unlock_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3367183dd..e721610a6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -118,6 +118,7 @@ The passwords do not match. \"Transformation rounds\" too high. Setting to 2147483648. Each string must have a field name. + This label already exists. Enter a positive whole number in the \"Length\" field. Could not enable autofill service. You can not move a group into itself. @@ -198,7 +199,6 @@ Install a web browser to open this URL. Open existing database Create new database - Recent databases Don\'t search through backup entries Omits \"Backup\" and \"Recycle bin\" groups from search results Quick search @@ -253,7 +253,7 @@ Warning Avoid password characters outside of text encoding format in database file (unrecognized chars are converted to the same letter). Grant file write access to save database changes - Mount the memory card to create or load a database. + Access to the file revoked by the file manager Continue without password unlocking protection? Continue without encryption key? Permanently delete selected nodes? @@ -283,6 +283,7 @@ Sign in with KeePassDX Enable autofilling to quickly fill out forms in other apps Set default autofill service + Autofill settings Generated password size Sets default size of the generated passwords Password characters @@ -298,6 +299,8 @@ Lock the database when the screen is off Press \'Back\' to lock Lock the database when the user clicks the back button on the root screen + Show lock button + Displays the lock button in the user interface Advanced unlock Use advanced unlocking to open a database more easily Biometric unlocking @@ -377,6 +380,8 @@ \"Go\" key action after pressing a \"Field\" key Vibratory keypresses Audible keypresses + Auto search + Automatically suggest search results from the web domain or applicationId Allow no master key Enable the \"Open\" button if no credentials are selected Delete password diff --git a/app/src/main/res/values/style_black.xml b/app/src/main/res/values/style_black.xml new file mode 100644 index 000000000..ea00d18f7 --- /dev/null +++ b/app/src/main/res/values/style_black.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 00517cb8e..d6a5c54eb 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -134,60 +134,6 @@ @style/KeepassDXStyle.Expanded.Title @style/KeepassDXStyle.Collapsed.Title - - - - - - - - - - + + + +