Compare commits

...

42 Commits

Author SHA1 Message Date
J-Jamet
9ddd66ce85 Merge tag '4.2.3' into develop
4.2.3
2025-10-29 18:37:28 +01:00
J-Jamet
e3b69789bf Merge branch 'release/4.2.3' 2025-10-29 18:37:19 +01:00
J-Jamet
54f2ed9fab fix: Small fixes 2025-10-29 18:27:54 +01:00
J-Jamet
2fea019b95 fix: Save search info if URL present #2255 2025-10-29 18:12:44 +01:00
J-Jamet
9ac7ef2d22 fix: Credential allow action during orientation change #2253 2025-10-29 17:12:27 +01:00
J-Jamet
6d452fa49c fix: Credential allow action #2253 2025-10-29 16:44:48 +01:00
J-Jamet
d99edb6b4d fix: database dialog subtitle #2254 2025-10-29 16:18:51 +01:00
J-Jamet
cb679f0d59 fix: Multiple Passkey selection #2253 2025-10-29 15:15:44 +01:00
J-Jamet
5dd9f75095 Merge tag '4.2.2' into develop
4.2.2
2025-10-28 17:44:51 +01:00
J-Jamet
403021d38b Merge branch 'release/4.2.2' 2025-10-28 17:44:43 +01:00
J-Jamet
fea7b30d6f Merge branch 'develop' into release/4.2.2 2025-10-28 17:19:08 +01:00
J-Jamet
ab5c859db4 fix: Tags in translations 2025-10-28 17:18:41 +01:00
J-Jamet
3fcbc65de0 Merge branch 'develop' of https://hosted.weblate.org/projects/keepass-dx/strings into translations 2025-10-28 17:16:39 +01:00
J-Jamet
3f1ee6bbea fix: Upgrade CHANGELOG 2025-10-28 17:14:44 +01:00
J-Jamet
37ce2ab781 fix: Snackbar error 2025-10-28 15:59:20 +01:00
J-Jamet
ffaf4a761a fix: Service stop 2025-10-28 15:48:07 +01:00
J-Jamet
56b7cc9118 fix: Progress message 2025-10-28 14:49:10 +01:00
J-Jamet
987f3f9047 fix: Revert fragment view 2025-10-28 14:15:19 +01:00
J-Jamet
3039efc67c fix: Cancelable during save 2025-10-28 14:07:32 +01:00
J-Jamet
26daac4637 fix: Dialog progress tasks 2025-10-28 13:28:20 +01:00
J-Jamet
88a93829a9 fix: Lock finish 2025-10-27 21:28:29 +01:00
J-Jamet
7923a63d36 fix: Database dialog stopped 2025-10-27 20:53:30 +01:00
J-Jamet
9a5c782d5d fix: Small fix, info dialog 2025-10-27 20:45:40 +01:00
J-Jamet
c39e4ba693 fix: Play service as privileged app for Passkey Cross Device Authentication #2244 2025-10-27 20:15:02 +01:00
J-Jamet
7db3d0502f fix: Save search info #2243 2025-10-27 19:32:00 +01:00
J-Jamet
d557e8b516 fix: merge algorithm #2223 2025-10-27 18:51:19 +01:00
Priit Jõerüüt
d6ae17657b Translated using Weblate (Estonian)
Currently translated at 100.0% (703 of 703 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/et/
2025-10-27 18:11:18 +01:00
solokot
3468b0f6f5 Translated using Weblate (Russian)
Currently translated at 100.0% (703 of 703 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ru/
2025-10-27 18:11:16 +01:00
J-Jamet
79777801e8 fix: merge algorithm 2025-10-27 16:34:38 +01:00
안세훈
a202f66d48 Translated using Weblate (Korean)
Currently translated at 52.6% (370 of 703 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/ko/
2025-10-27 03:02:44 +00:00
Kunzisoft
ba58d5d47c Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 33.3% (1 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/zh_Hant/
2025-10-25 16:03:05 +00:00
Kunzisoft
46685592df Translated using Weblate (Croatian)
Currently translated at 33.3% (1 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/hr/
2025-10-25 16:03:03 +00:00
Kunzisoft
ba9e2892ef Translated using Weblate (Indonesian)
Currently translated at 33.3% (1 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/id/
2025-10-25 16:03:02 +00:00
Kunzisoft
a1da3b4fbd Translated using Weblate (Italian)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/it/
2025-10-25 16:03:01 +00:00
Kunzisoft
8bee0ec220 Translated using Weblate (Macedonian)
Currently translated at 33.3% (1 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/mk/
2025-10-25 16:02:59 +00:00
Kunzisoft
aebf6b21de Translated using Weblate (Arabic)
Currently translated at 66.6% (2 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ar/
2025-10-25 16:02:58 +00:00
Kunzisoft
0cf9253ea4 Translated using Weblate (Spanish)
Currently translated at 33.3% (1 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/es/
2025-10-25 16:02:55 +00:00
Kunzisoft
b63ceb37a4 Translated using Weblate (Turkish)
Currently translated at 100.0% (3 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/tr/
2025-10-25 16:02:54 +00:00
solokot
c462dae6f5 Translated using Weblate (Russian)
Currently translated at 66.6% (2 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ru/
2025-10-25 16:02:52 +00:00
Kunzisoft
ddf890b861 Translated using Weblate (Russian)
Currently translated at 66.6% (2 of 3 strings)

Translation: KeePassDX/Metadata
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/metadata/ru/
2025-10-25 16:02:51 +00:00
Besnik Bleta
252eb30b13 Translated using Weblate (Albanian)
Currently translated at 92.0% (647 of 703 strings)

Translation: KeePassDX/Strings
Translate-URL: https://hosted.weblate.org/projects/keepass-dx/strings/sq/
2025-10-25 16:02:43 +00:00
J-Jamet
62ab11cc56 Merge tag '4.2.1' into develop
4.2.1
2025-10-24 18:16:25 +02:00
48 changed files with 503 additions and 280 deletions

View File

@@ -1,3 +1,15 @@
KeePassDX(4.2.3)
* Fix multiple Passkey selection #2253
* Fix database dialog subtitle #2254
* Fix save search info if URL present #2255
* Small fixes
KeePassDX(4.2.2)
* Fix database merge algorithm #2223
* Fix save search info #2243
* Fix Play Service as privileged app for Passkey Cross Device Authentication #2244
* Small fixes
KeePassDX(4.2.1)
* Fix Magikeyboard autosearch #2233
* Fix database merge #2223

View File

@@ -11,8 +11,8 @@ android {
applicationId "com.kunzisoft.keepass"
minSdkVersion 19
targetSdkVersion 35
versionCode = 146
versionName = "4.2.1"
versionCode = 148
versionName = "4.2.3"
multiDexEnabled true
testApplicationId = "com.kunzisoft.keepass.tests"

View File

@@ -125,6 +125,8 @@ class EntryActivity : DatabaseLockActivity() {
private var mBackgroundColor: Int? = null
private var mForegroundColor: Int? = null
override fun manageDatabaseInfo(): Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -157,6 +157,8 @@ class EntryEditActivity : DatabaseLockActivity(),
}
}
override fun manageDatabaseInfo(): Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_entry_edit)

View File

@@ -94,6 +94,8 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
private var mExternalFileHelper: ExternalFileHelper? = null
override fun manageDatabaseInfo(): Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -250,13 +252,13 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
database,
false
)
coordinatorLayout.showActionErrorIfNeeded(result)
}
ACTION_DATABASE_LOAD_TASK -> {
launchGroupActivityIfLoaded(database)
}
}
}
coordinatorLayout.showActionErrorIfNeeded(result)
}
/**

View File

@@ -274,6 +274,8 @@ class GroupActivity : DatabaseLockActivity(),
mGroupEditViewModel.selectIcon(icon)
}
override fun manageDatabaseInfo(): Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -839,7 +841,7 @@ class GroupActivity : DatabaseLockActivity(),
// Open child group
loadMainGroup(GroupState(group.nodeId, 0))
} catch (e: ClassCastException) {
Log.e(TAG, "Node can't be cast in Group")
Log.e(TAG, "Node can't be cast in Group", e)
}
Type.ENTRY -> try {
@@ -865,14 +867,16 @@ class GroupActivity : DatabaseLockActivity(),
if (!database.isReadOnly
&& searchInfo != null
&& PreferencesUtil.isKeyboardSaveSearchInfoEnable(this@GroupActivity)
&& entryVersioned.containsSearchInfo(database, searchInfo).not()
) {
updateEntryWithRegisterInfo(
database,
entryVersioned,
searchInfo.toRegisterInfo()
)
} else {
entrySelectedForKeyboardSelection(database, entryVersioned)
}
entrySelectedForKeyboardSelection(database, entryVersioned)
}
TypeMode.PASSKEY -> {
entrySelectedForPasskeySelection(database, entryVersioned)
@@ -881,14 +885,16 @@ class GroupActivity : DatabaseLockActivity(),
if (!database.isReadOnly
&& searchInfo != null
&& PreferencesUtil.isAutofillSaveSearchInfoEnable(this@GroupActivity)
&& entryVersioned.containsSearchInfo(database, searchInfo).not()
) {
updateEntryWithRegisterInfo(
database,
entryVersioned,
searchInfo.toRegisterInfo()
)
} else {
entrySelectedForAutofillSelection(database, entryVersioned)
}
entrySelectedForAutofillSelection(database, entryVersioned)
}
}
loadGroup()
@@ -908,7 +914,7 @@ class GroupActivity : DatabaseLockActivity(),
finish()
})
} catch (e: ClassCastException) {
Log.e(TAG, "Node can't be cast in Entry")
Log.e(TAG, "Node can't be cast in Entry", e)
}
}
}
@@ -977,6 +983,17 @@ class GroupActivity : DatabaseLockActivity(),
updateEntry(entry, newEntry)
}
private fun Entry.containsSearchInfo(
database: ContextualDatabase,
searchInfo: SearchInfo
): Boolean {
return getEntryInfo(
database,
raw = true,
removeTemplateConfiguration = false
).containsSearchInfo(searchInfo)
}
private fun finishNodeAction() {
actionNodeMode?.finish()
}

View File

@@ -78,6 +78,8 @@ class IconPickerActivity : DatabaseLockActivity() {
private var mExternalFileHelper: ExternalFileHelper? = null
override fun manageDatabaseInfo(): Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -45,6 +45,8 @@ class ImageViewerActivity : DatabaseLockActivity() {
private lateinit var imageView: ImageView
private lateinit var progressView: View
override fun manageDatabaseInfo(): Boolean = false
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -28,6 +28,8 @@ class KeyGeneratorActivity : DatabaseLockActivity() {
private lateinit var validationButton: View
private var lockView: View? = null
override fun manageDatabaseInfo(): Boolean = true
private val keyGeneratorViewModel: KeyGeneratorViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -124,6 +124,8 @@ class MainCredentialActivity : DatabaseModeActivity() {
private var mReadOnly: Boolean = false
private var mForceReadOnly: Boolean = false
override fun manageDatabaseInfo(): Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -1,13 +1,17 @@
package com.kunzisoft.keepass.activities.legacy
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
@@ -16,13 +20,14 @@ import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment
import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Companion.DATABASE_CHANGED_DIALOG_TAG
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.DatabaseTaskProvider.Companion.startDatabaseService
import com.kunzisoft.keepass.database.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment
import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS_TASK_DIALOG_TAG
import com.kunzisoft.keepass.tasks.ProgressTaskViewModel
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
import kotlinx.coroutines.launch
@@ -32,6 +37,7 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
protected val mDatabase: ContextualDatabase?
get() = mDatabaseViewModel.database
private val progressTaskViewModel: ProgressTaskViewModel by viewModels()
private var progressTaskDialogFragment: ProgressTaskDialogFragment? = null
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
@@ -53,49 +59,104 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
}
}
/**
* Useful to only waiting for the activity result and prevent any parallel action
*/
var credentialResultLaunched = false
/**
* Utility activity result launcher,
* Used recursively, close each activity with return data
*/
protected var mCredentialActivityResultLauncher: CredentialActivityResultLauncher =
CredentialActivityResultLauncher(
registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
setActivityResult(
lockDatabase = false,
resultCode = it.resultCode,
data = it.data
)
}
)
/**
* Custom ActivityResultLauncher to manage the database action
*/
protected inner class CredentialActivityResultLauncher(
val builder: ActivityResultLauncher<Intent>
) : ActivityResultLauncher<Intent>() {
override fun launch(
input: Intent?,
options: ActivityOptionsCompat?
) {
credentialResultLaunched = true
builder.launch(input, options)
}
override fun unregister() {
builder.unregister()
}
override fun getContract(): ActivityResultContract<Intent?, *> {
return builder.getContract()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null
&& savedInstanceState.containsKey(CREDENTIAL_RESULT_LAUNCHER_KEY)
) {
credentialResultLaunched = savedInstanceState.getBoolean(CREDENTIAL_RESULT_LAUNCHER_KEY)
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mDatabaseViewModel.actionState.collect { uiState ->
when (uiState) {
is DatabaseViewModel.ActionState.Loading -> {}
is DatabaseViewModel.ActionState.OnDatabaseReloaded -> {
if (finishActivityIfReloadRequested()) {
finish()
if (credentialResultLaunched.not()) {
when (uiState) {
is DatabaseViewModel.ActionState.Wait -> {}
is DatabaseViewModel.ActionState.OnDatabaseReloaded -> {
if (finishActivityIfReloadRequested()) {
finish()
}
}
}
is DatabaseViewModel.ActionState.OnDatabaseInfoChanged -> {
if (manageDatabaseInfo()) {
showDatabaseChangedDialog(
uiState.previousDatabaseInfo,
uiState.newDatabaseInfo,
uiState.readOnlyDatabase
is DatabaseViewModel.ActionState.OnDatabaseInfoChanged -> {
if (manageDatabaseInfo()) {
showDatabaseChangedDialog(
uiState.previousDatabaseInfo,
uiState.newDatabaseInfo,
uiState.readOnlyDatabase
)
}
}
is DatabaseViewModel.ActionState.OnDatabaseActionRequested -> {
startDatabasePermissionService(
uiState.bundle,
uiState.actionTask
)
}
}
is DatabaseViewModel.ActionState.OnDatabaseActionRequested -> {
startDatabasePermissionService(
uiState.bundle,
uiState.actionTask
)
}
is DatabaseViewModel.ActionState.OnDatabaseActionStarted -> {
showDialog(uiState.progressMessage)
}
is DatabaseViewModel.ActionState.OnDatabaseActionUpdated -> {
showDialog(uiState.progressMessage)
}
is DatabaseViewModel.ActionState.OnDatabaseActionStopped -> {
// nothing here, wait for the action to finish
}
is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
uiState.database,
uiState.actionTask,
uiState.result
)
stopDialog()
is DatabaseViewModel.ActionState.OnDatabaseActionStarted -> {
progressTaskViewModel.show(uiState.progressMessage)
}
is DatabaseViewModel.ActionState.OnDatabaseActionUpdated -> {
progressTaskViewModel.show(uiState.progressMessage)
}
is DatabaseViewModel.ActionState.OnDatabaseActionStopped -> {
progressTaskViewModel.hide()
}
is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> {
onDatabaseActionFinished(
uiState.database,
uiState.actionTask,
uiState.result
)
progressTaskViewModel.hide()
}
}
}
}
@@ -103,15 +164,34 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
mDatabaseViewModel.databaseState.collect { database ->
// Nullable function
onUnknownDatabaseRetrieved(database)
database?.let {
onDatabaseRetrieved(database)
progressTaskViewModel.progressTaskState.collect { state ->
when (state) {
is ProgressTaskViewModel.ProgressTaskState.Show ->
startDialog()
is ProgressTaskViewModel.ProgressTaskState.Hide ->
stopDialog()
}
}
}
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
mDatabaseViewModel.databaseState.collect { database ->
if (credentialResultLaunched.not()) {
// Nullable function
onUnknownDatabaseRetrieved(database)
database?.let {
onDatabaseRetrieved(database)
}
}
}
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putBoolean(CREDENTIAL_RESULT_LAUNCHER_KEY, credentialResultLaunched)
super.onSaveInstanceState(outState)
}
/**
@@ -194,7 +274,7 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
}
}
private fun showDialog(progressMessage: ProgressMessage) {
private fun startDialog() {
lifecycleScope.launch {
if (showDatabaseDialog()) {
if (progressTaskDialogFragment == null) {
@@ -208,12 +288,6 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
PROGRESS_TASK_DIALOG_TAG
)
}
progressTaskDialogFragment?.apply {
updateTitle(progressMessage.titleId)
updateMessage(progressMessage.messageId)
updateWarning(progressMessage.warningId)
setCancellable(progressMessage.cancelable)
}
}
}
}
@@ -226,4 +300,8 @@ abstract class DatabaseActivity : StylishActivity(), DatabaseRetrieval {
protected open fun showDatabaseDialog(): Boolean {
return true
}
companion object {
const val CREDENTIAL_RESULT_LAUNCHER_KEY = "com.kunzisoft.keepass.CREDENTIAL_RESULT_LAUNCHER_KEY"
}
}

View File

@@ -370,9 +370,11 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.lock) { _, _ ->
sendBroadcast(Intent(LOCK_ACTION))
finish()
}.create().show()
} else {
sendBroadcast(Intent(LOCK_ACTION))
finish()
}
}

View File

@@ -1,12 +1,9 @@
package com.kunzisoft.keepass.activities.legacy
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.isIntentSenderMode
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.removeInfo
@@ -15,7 +12,6 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveReg
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSpecialMode
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveTypeMode
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
import com.kunzisoft.keepass.credentialprovider.SpecialMode
import com.kunzisoft.keepass.credentialprovider.TypeMode
import com.kunzisoft.keepass.model.RegisterInfo
@@ -34,21 +30,6 @@ abstract class DatabaseModeActivity : DatabaseActivity() {
private var mToolbarSpecial: ToolbarSpecial? = null
/**
* Utility activity result launcher,
* Used recursively, close each activity with return data
*/
protected open var mCredentialActivityResultLauncher: ActivityResultLauncher<Intent>? =
registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
setActivityResult(
lockDatabase = false,
resultCode = it.resultCode,
data = it.data
)
}
open fun onDatabaseBackPressed() {
if (mSpecialMode != SpecialMode.DEFAULT)
onCancelSpecialMode()

View File

@@ -70,13 +70,11 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
autofillLauncherViewModel.manageRegistrationResult(it)
}
override fun applyCustomStyle(): Boolean {
return false
}
override fun applyCustomStyle(): Boolean = false
override fun finishActivityIfReloadRequested(): Boolean {
return true
}
override fun finishActivityIfReloadRequested(): Boolean = true
override fun manageDatabaseInfo(): Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
// To apply the bypass https://github.com/Kunzisoft/KeePassDX/issues/2238

View File

@@ -56,6 +56,8 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
override fun finishActivityIfReloadRequested() = false
override fun manageDatabaseInfo(): Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
entrySelectionViewModel.initialize()
@@ -76,6 +78,7 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
context = this@EntrySelectionLauncherActivity,
searchInfo = uiState.searchInfo
)
finish()
}
is EntrySelectionViewModel.UIState.LaunchGroupActivityForSearch -> {
GroupActivity.launchForSearch(
@@ -83,6 +86,7 @@ class EntrySelectionLauncherActivity : DatabaseModeActivity() {
database = uiState.database,
searchInfo = uiState.searchInfo
)
finish()
}
}
}

View File

@@ -121,7 +121,6 @@ class DatabaseTaskProvider(
}
private fun initServiceConnection() {
actionTaskListener?.onActionStopped()
if (serviceConnection == null) {
serviceConnection = object : ServiceConnection {
override fun onBindingDied(name: ComponentName?) {

View File

@@ -4,7 +4,7 @@ import androidx.annotation.StringRes
data class ProgressMessage(
@StringRes
var titleId: Int,
var titleId: Int? = null,
@StringRes
var messageId: Int? = null,
@StringRes

View File

@@ -165,7 +165,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
}
}
if (attachmentNotificationList.isEmpty()) {
stopSelf()
stopService()
}
}
}

View File

@@ -62,7 +62,7 @@ class ClipboardEntryNotificationService : LockNotificationService() {
sendBroadcast(Intent(LOCK_ACTION))
}
// Stop the service
stopSelf()
stopService()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

View File

@@ -262,11 +262,12 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
)
}
} else {
/* Do not stopped here, service cannot be connected
mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onActionStopped(
database
)
}
}*/
}
}
}
@@ -338,7 +339,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
val intentAction = intent?.action
if (intentAction == null && !database.loaded) {
stopSelf()
stopService()
}
val actionRunnable: ActionRunnable? = when (intentAction) {
@@ -447,10 +448,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
TimeoutHelper.releaseTemporarilyDisableTimeout()
// Stop service after save if user remove task
if (save && mTaskRemovedRequested) {
actionOnLock()
stopService()
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
if (!database.loaded) {
stopSelf()
stopService()
} else {
// Restart the service to open lock notification
try {
@@ -535,11 +536,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
val notificationBuilder = buildNewNotification().apply {
setSmallIcon(iconId)
intent?.let {
setContentTitle(getString(
intent.getIntExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId))
)
}
val titleId = mProgressMessage.titleId?.let {
intent?.getIntExtra(DATABASE_TASK_TITLE_KEY, it)
} ?: R.string.app_name
setContentTitle(getString(titleId))
setAutoCancel(false)
setContentIntent(null)
}
@@ -661,7 +661,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
private fun updateMessage(resId: Int) {
mProgressMessage.messageId = resId
mProgressMessage = mProgressMessage.copy(
messageId = resId
)
notifyProgressMessage()
}
@@ -673,7 +675,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
updateMessage(R.string.decrypting_db)
}
override fun actionOnLock() {
override fun stopService() {
if (!TimeoutHelper.temporarilyDisableLock) {
closeDatabase(mDatabase)
// Remove the database during the lock
@@ -685,7 +687,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// Remove the lock timer (no more needed if it exists)
TimeoutHelper.cancelLockTimer(this)
// Service is stopped after receive the broadcast
super.actionOnLock()
super.stopService()
}
}
@@ -729,7 +731,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// Close channels
closeChallengeResponse()
// Restore previous message
mProgressMessage = previousMessage
mProgressMessage = previousMessage.apply {
cancelable = null
}
notifyProgressMessage()
}
return response

View File

@@ -55,7 +55,7 @@ class KeyboardEntryNotificationService : LockNotificationService() {
sendBroadcast(Intent(LOCK_ACTION))
}
// Stop the service
stopSelf()
stopService()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

View File

@@ -20,7 +20,6 @@
package com.kunzisoft.keepass.services
import android.content.Intent
import androidx.core.app.ServiceCompat
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.LockReceiver
import com.kunzisoft.keepass.utils.registerLockReceiver
@@ -29,13 +28,7 @@ import com.kunzisoft.keepass.utils.unregisterLockReceiver
abstract class LockNotificationService : NotificationService() {
private var mLockReceiver: LockReceiver = LockReceiver {
actionOnLock()
}
protected open fun actionOnLock() {
// Stop the service in all cases
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()
stopService()
}
override fun onCreate() {
@@ -46,7 +39,7 @@ abstract class LockNotificationService : NotificationService() {
override fun onTaskRemoved(rootIntent: Intent?) {
if (!TimeoutHelper.temporarilyDisableLock) {
actionOnLock()
stopService()
}
super.onTaskRemoved(rootIntent)
}

View File

@@ -17,6 +17,7 @@ import android.util.TypedValue
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.Stylish
@@ -114,6 +115,12 @@ abstract class NotificationService : Service() {
}
}
protected open fun stopService() {
// Stop the service in all cases
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()
}
protected fun defineTimerJob(builder: NotificationCompat.Builder,
type: NotificationServiceType,
timeoutMilliseconds: Long,

View File

@@ -13,6 +13,8 @@ abstract class ExternalSettingsActivity : DatabaseModeActivity() {
private var lockView: FloatingActionButton? = null
override fun manageDatabaseInfo(): Boolean = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -27,32 +27,27 @@ import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.kunzisoft.keepass.R
import kotlinx.coroutines.launch
open class ProgressTaskDialogFragment : DialogFragment() {
@StringRes
private var title = UNDEFINED
@StringRes
private var message = UNDEFINED
@StringRes
private var warning = UNDEFINED
private var cancellable: (() -> Unit)? = null
private var titleView: TextView? = null
private var messageView: TextView? = null
private var warningView: TextView? = null
private var cancelButton: Button? = null
private var progressView: ProgressBar? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
private val progressTaskViewModel: ProgressTaskViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
try {
activity?.let {
val builder = AlertDialog.Builder(it)
@@ -71,68 +66,63 @@ open class ProgressTaskDialogFragment : DialogFragment() {
cancelButton = root.findViewById(R.id.progress_dialog_cancel)
progressView = root.findViewById(R.id.progress_dialog_bar)
updateTitle(title)
updateMessage(message)
updateWarning(warning)
setCancellable(cancellable)
isCancelable = false
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
progressTaskViewModel.progressTaskState.collect { state ->
when (state) {
is ProgressTaskViewModel.ProgressTaskState.Show -> {
val value = state.value
updateView(
titleView,
value.titleId?.let { title ->
getString(title)
})
updateView(
messageView,
value.messageId?.let { message ->
getString(message)
})
updateView(
warningView,
value.warningId?.let { warning ->
getString(warning)
})
cancelButton?.apply {
isVisible = value.cancelable != null
setOnClickListener {
value.cancelable?.invoke()
}
}
}
else -> {
// Nothing here, this fragment is stopped externally
}
}
}
}
}
return builder.create()
}
} catch (e: Exception) {
Log.e(TAG, "Unable to create progress dialog")
Log.e(TAG, "Unable to create progress dialog", e)
}
return super.onCreateDialog(savedInstanceState)
}
fun setTitle(@StringRes titleId: Int) {
this.title = titleId
}
private fun updateView(textView: TextView?, @StringRes resId: Int) {
activity?.lifecycleScope?.launch {
if (resId == UNDEFINED) {
textView?.visibility = View.GONE
} else {
textView?.setText(resId)
textView?.visibility = View.VISIBLE
}
private fun updateView(textView: TextView?, value: String?) {
if (value == null) {
textView?.visibility = View.GONE
} else {
textView?.text = value
textView?.visibility = View.VISIBLE
}
}
private fun updateCancelable() {
activity?.lifecycleScope?.launch {
cancelButton?.isVisible = cancellable != null
cancelButton?.setOnClickListener {
cancellable?.invoke()
}
}
}
fun updateTitle(@StringRes resId: Int?) {
this.title = resId ?: UNDEFINED
updateView(titleView, title)
}
fun updateMessage(@StringRes resId: Int?) {
this.message = resId ?: UNDEFINED
updateView(messageView, message)
}
fun updateWarning(@StringRes resId: Int?) {
this.warning = resId ?: UNDEFINED
updateView(warningView, warning)
}
fun setCancellable(cancellable: (() -> Unit)?) {
this.cancellable = cancellable
updateCancelable()
}
companion object {
private val TAG = ProgressTaskDialogFragment::class.java.simpleName
const val PROGRESS_TASK_DIALOG_TAG = "progressDialogFragment"
const val UNDEFINED = -1
}
}

View File

@@ -0,0 +1,28 @@
package com.kunzisoft.keepass.tasks
import androidx.lifecycle.ViewModel
import com.kunzisoft.keepass.database.ProgressMessage
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
class ProgressTaskViewModel: ViewModel() {
private val mProgressTaskState = MutableStateFlow<ProgressTaskState>(ProgressTaskState.Hide)
val progressTaskState: StateFlow<ProgressTaskState> = mProgressTaskState
fun show(value: ProgressMessage) {
mProgressTaskState.update { currentState ->
ProgressTaskState.Show(value)
}
}
fun hide() {
mProgressTaskState.value = ProgressTaskState.Hide
}
sealed class ProgressTaskState {
data class Show(val value: ProgressMessage): ProgressTaskState()
object Hide: ProgressTaskState()
}
}

View File

@@ -98,25 +98,43 @@ object AppUtil {
}
val processedPackageNames = mutableSetOf<String>()
for (resolveInfo in resolveInfoList) {
val packageName = resolveInfo.activityInfo.packageName
if (packageName != null && !processedPackageNames.contains(packageName)) {
try {
val packageInfo = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNING_CERTIFICATES
)
val signatureFingerprints = packageInfo.signingInfo?.getAllFingerprints()
signatureFingerprints?.let {
browserList.add(AndroidPrivilegedApp(packageName, signatureFingerprints))
processedPackageNames.add(packageName)
}
} catch (e: Exception) {
Log.e(AppUtil::class.simpleName, "Error processing package: $packageName", e)
buildAndroidPrivilegedApp(packageManager, packageName)?.let { privilegedApp ->
browserList.add(privilegedApp)
processedPackageNames.add(packageName)
}
}
}
// Add the Play Service
val gServices = "com.google.android.gms"
buildAndroidPrivilegedApp(packageManager, gServices)?.let { privilegedApp ->
browserList.add(privilegedApp)
processedPackageNames.add(gServices)
}
return browserList.distinctBy { it.packageName } // Ensure uniqueness just in case
}
@RequiresApi(Build.VERSION_CODES.P)
private fun buildAndroidPrivilegedApp(
packageManager: PackageManager,
packageName: String
): AndroidPrivilegedApp? {
return try {
val packageInfo = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNING_CERTIFICATES
)
val signatureFingerprints = packageInfo.signingInfo?.getAllFingerprints()
signatureFingerprints?.let {
AndroidPrivilegedApp(packageName, signatureFingerprints)
}
} catch (e: Exception) {
Log.e(AppUtil::class.simpleName, "Error processing package: $packageName", e)
null
}
}
}

View File

@@ -32,7 +32,7 @@ class DatabaseViewModel(application: Application): AndroidViewModel(application)
val database: ContextualDatabase?
get() = databaseState.value
private val mActionState = MutableStateFlow<ActionState>(ActionState.Loading)
private val mActionState = MutableStateFlow<ActionState>(ActionState.Wait)
val actionState: StateFlow<ActionState> = mActionState
private var mDatabaseTaskProvider: DatabaseTaskProvider = DatabaseTaskProvider(
@@ -469,7 +469,7 @@ class DatabaseViewModel(application: Application): AndroidViewModel(application)
}
sealed class ActionState {
object Loading: ActionState()
object Wait: ActionState()
object OnDatabaseReloaded: ActionState()
data class OnDatabaseActionRequested(
val bundle: Bundle? = null,

View File

@@ -666,4 +666,41 @@
<string name="passkeys_close_database_title">Sulge andmebaas</string>
<string name="passkeys_privileged_apps_ask_title">Rakendust ei õnnestu tuvastada</string>
<string name="warning_overwrite_data_title">Kas soovid olemasolevad andmed üle kirjutada?</string>
<string name="warning_overwrite_data_description">Selle toiminguga asendad kirje olemasolevad andmed, aga kui ajaloo logimine on kasutusel, siis saad vanu kirjeid ka hiljem näha.</string>
<string name="credential_provider">kasutajanime/salasõna automaattäite teenus</string>
<string name="passkeys">WebAuthn pääsuvõtmed</string>
<string name="passkeys_explanation_summary">Seadista WebAuthn pääsuvõtmed, mis võimaldavad kiiret ja turvalist salasõnadeta ligipääsu</string>
<string name="passkeys_preference_title">Pääsuvõtmete seadistused</string>
<string name="passkeys_close_database_summary">Peale pääsuvõtme valimist sulge andmebaas</string>
<string name="passkeys_privileged_apps_title">Eesõigustega rakendused</string>
<string name="passkeys_privileged_apps_summary">Halda brausereid sinu loodud eesõigustega rakenduste loendis</string>
<string name="passkeys_privileged_apps_explanation">HOIATUS: Eesõigustega rakendus toimib lüüsina autentimise algallikaga suhtlemisel. Turvaprobleemide vältimiseks palun taga, et kasutad õiget rakendust.</string>
<string name="passkeys_privileged_apps_ask_message">%1$s kaset pääsuvõtmega autentimiseks.\n\nKas lisame ta eesõigustega rakenduste loendisse?</string>
<string name="passkeys_missing_signature_app_ask_title">Allkiri on puudu</string>
<string name="passkeys_missing_signature_app_ask_explanation">HOAITUS: See pääsuluba on loodud teise kliendi poolt või on allkiri kustutatud. Turvaprobleemide vältimiseks palun kontrolli rakendus, milles soovi end tuvastada on sama teenuse osa ning sisuliselt õige.\n\nKui rakendus on veebibrauser, siis ära lisa allkirja kirje juurde vaid seadistustes leiduva eesõigustega rakenduste loendisse.</string>
<string name="passkeys_missing_signature_app_ask_message">%1$s on antud kontekstis tundmatu ja mittetunnustatud ning ta proovib autentimist olemasoleva pääsuvõtmega.</string>
<string name="passkeys_missing_signature_app_ask_question">Kas soovid lisada rakenduse allkirja pääsuvõtme kirjele?</string>
<string name="passkeys_auto_select_title">Automaatne valik</string>
<string name="passkeys_auto_select_summary">Vali automaatselt vaid siis, kui vaid üks kirje ja andmebaas on avatud ning vaid siis, kui rakendus ühildub</string>
<string name="passkeys_backup_eligibility_title">Varunduse kõlblikkus</string>
<string name="passkeys_backup_eligibility_summary">Otsusta loomise hetkel, kas avaliku võtme allikat on lubatud varundada</string>
<string name="passkeys_backup_state_title">Varunduse olek</string>
<string name="passkeys_backup_state_summary">Anna märku, kas autentimisandmed on varundatud ja kaitstud üksiku seadme kaotsimineku puhul</string>
<string name="credential_provider_service_subtitle">Pääsuvõtmed - automaattäite teenusepakkuja</string>
<string name="passkey">WebAuthn pääsuvõti</string>
<string name="passkey_service_name">KeePassDX autentimisteenusepakkuja</string>
<string name="passkey_creation_description">Salvesta pääsuvõti uue kirjena</string>
<string name="passkey_update_description">Uuenda pääsuvõtit: %1$s</string>
<string name="passkey_selection_username">Pääsuvõtit ei leidu</string>
<string name="passkey_selection_description">Vali olemasolev pääsuvõti</string>
<string name="passkey_database_username">KeePassDX-i andmebaas</string>
<string name="passkey_locked_database_description">Vali lukustuse eemaldamiseks</string>
<string name="passkey_username">Pääsuvõtme kasutajanimi</string>
<string name="passkey_private_key">Pääsuvõtme privaatvõti</string>
<string name="passkey_credential_id">Pääsuvõtme autentimisühiku tunnus</string>
<string name="passkey_user_handle">Pääsuvõtme kasutaja võrgutunnus</string>
<string name="passkey_relying_party">Pääsuvõtit edastav osapool</string>
<string name="passkey_backup_eligibility">Pääsuvõtme kõlblikkus varundamiseks</string>
<string name="passkey_backup_state">Pääsuvõtme varunduse olek</string>
<string name="error_passkey_result">Pääsuvõtme väljastamine vastuseks ei õnnestu</string>
</resources>

View File

@@ -19,7 +19,7 @@
--><resources>
<string name="feedback">피드백</string>
<string name="homepage">홈페이지</string>
<string name="about_description">KeePass 암호 관리자의 Android 구현</string>
<string name="about_description">KeePass 암호 관리자의 Android 구현체.</string>
<string name="accept">확인</string>
<string name="add_entry">항목 추가</string>
<string name="edit_entry">항목 수정</string>
@@ -40,14 +40,13 @@
<string name="clipboard_error_clear">클립보드를 비울 수 없음</string>
<string name="clipboard_timeout">클립보드 시간 초과</string>
<string name="clipboard_timeout_summary">클립보드 저장이 유지될 시간 (장치가 지원한다면)</string>
<string name="select_to_copy">%1$s 을(를) 클립보드에 복사하려면 선택하십시오.</string>
<string name="select_to_copy">%1$s 을(를) 클립보드에 복사하</string>
<string name="retrieving_db_key">데이터베이스 키를 검색하는 중…</string>
<string name="database">데이터베이스</string>
<string name="decrypting_db">데이터베이스 컨텐츠 암호 해독 중…</string>
<string name="default_checkbox">기본 데이터베이스로 사용</string>
<string name="digits">단위</string>
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft는 보증이 적용되지 않습니다; 이것은 자유 소프트웨어이며, 광고가 없습니다.
\n이 것은 보증 없이 있는 그대로, GPL 버전 3하에 제공됩니다.</string>
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft는 보증이 적용되지 않습니다; 이것은 &lt;strong&gt;자유 소프트웨어&lt;/strong&gt;이며, &lt;strong&gt;광고가 없습니다&lt;/strong&gt;. \n이 것은 보증 없이 있는 그대로, &lt;strong&gt;GPL 버전 3&lt;/strong&gt; 라이선스로 제공됩니다.</string>
<string name="entry_accessed">접근됨</string>
<string name="entry_cancel">취소</string>
<string name="entry_notes">노트</string>
@@ -81,22 +80,22 @@
<string name="field_name">필드 이름</string>
<string name="field_value">필드 값</string>
<string name="file_not_found_content">파일을 찾을 수 없습니다. 파일 탐색기에서 열리는지 확인해 주세요.</string>
<string name="file_browser">파일 탐색기</string>
<string name="file_browser">파일 관리자</string>
<string name="generate_password">비밀번호 생성</string>
<string name="hint_conf_pass">비밀번호 확인</string>
<string name="hint_generated_password">생성된 비밀번호</string>
<string name="hint_conf_pass">패스워드 확인</string>
<string name="hint_generated_password">생성된 패스워드</string>
<string name="hint_group_name">그룹 이름</string>
<string name="hint_keyfile">키 파일</string>
<string name="hint_length">길이</string>
<string name="hint_pass">비밀번호</string>
<string name="hint_pass">패스워드</string>
<string name="password">비밀번호</string>
<string name="invalid_credentials">비밀번호나 키 파일을 읽을 수 없습니다.</string>
<string name="invalid_credentials">패스워드나 키 파일을 읽을 수 없습니다.</string>
<string name="invalid_algorithm">잘못된 알고리즘입니다.</string>
<string name="invalid_db_sig">데이터베이스 형식을 인식할 수 없습니다.</string>
<string name="keyfile_is_empty">이 키 파일은 비어 있습니다.</string>
<string name="length">길이</string>
<string name="list_entries_show_username_title">아이디 보이기</string>
<string name="list_entries_show_username_summary">엔트리 목록에 아이디 보이기</string>
<string name="list_entries_show_username_summary">항목 리스트에 사용자 이름을 표시합니다</string>
<string name="list_size_title">리스트 항목 크기</string>
<string name="list_size_summary">요소 목록 텍스트 크기</string>
<string name="loading_database">데이터베이스 로딩 중…</string>
@@ -105,7 +104,7 @@
<string name="hide_password_summary">기본 비밀번호를 (***) 로 가리기</string>
<string name="about">정보</string>
<string name="menu_change_key_settings">마스터 키 바꾸기</string>
<string name="copy_field">%1$s 복사됨</string>
<string name="copy_field">%1$s의 사본</string>
<string name="settings">설정</string>
<string name="menu_app_settings">앱 설정</string>
<string name="menu_form_filling_settings">폼 채우기</string>
@@ -129,8 +128,8 @@
<string name="never">절대 하지 않음</string>
<string name="no_results">검색 결과가 없음</string>
<string name="no_url_handler">이 URL을 열기 위해 웹 브라우저를 설치하십시오.</string>
<string name="select_database_file">가지고 있는 데이터베이스 열기</string>
<string name="create_keepass_file">데이터베이스 생성</string>
<string name="select_database_file">기존 저장소 열기</string>
<string name="create_keepass_file">저장소 생성</string>
<string name="progress_create">새 데이터베이스 생성 중…</string>
<string name="progress_title">작업 중…</string>
<string name="protection">보호</string>
@@ -138,7 +137,7 @@
<string name="read_only_warning">KeePassDX는 데이터베이스를 수정하기 위해 쓰기 권한이 필요합니다.</string>
<string name="content_description_remove_from_list">삭제</string>
<string name="root">루트</string>
<string name="encryption_explanation">데이터베이스 암호화 알고리즘이 모든 데이터에 적용됩니다.</string>
<string name="encryption_explanation">데이터베이스 암호화 알고리즘이 모든 데이터에 적용됩니다</string>
<string name="memory_usage">메모리 사용량</string>
<string name="memory_usage_explanation">바이너리 바이트 단위의 메모리 용량이 키 파생 기능에 사용됩니다.</string>
<string name="saving_database">데이터베이스 저장 중…</string>
@@ -250,10 +249,10 @@
<string name="error_field_name_already_exists">그 필드 이름은 이미 존재합니다.</string>
<string name="inherited">상속</string>
<string name="auto_type">자동입력</string>
<string name="warning_database_already_opened">데이터베이스가 이미 열려있습니다. 새 것을 열려면 지금 것을 먼저 닫아주세요.</string>
<string name="warning_database_already_opened">데이터베이스가 이미 열려있습니다. 새 것을 열려면 지금 것을 먼저 닫아주세요</string>
<string name="hide_broken_locations_summary">최근 데이터베이스 목록에서 파손된 링크를 감춤</string>
<string name="import_app_properties_summary">앱 속성을 내보낼 파일 선택</string>
<string name="success_import_app_properties">속성을 가져왔습니다.</string>
<string name="import_app_properties_summary">가져올 앱 설정 파일 선택</string>
<string name="success_import_app_properties">설정 가져옴</string>
<string name="kdf_explanation">암호화 알고리즘 용 키를 생성하기 위해, 마스터키는 임의의 솔트(salt) 키 파생 함수를 사용하여 변환됩니다.</string>
<string name="warning">경고</string>
<string name="error_challenge_already_requested">이미 요청된 시도입니다.</string>
@@ -262,14 +261,14 @@
<string name="error_response_already_provided">이미 응답했습니다.</string>
<string name="error_location_unknown">데이터 베이스 위치를 알 수 없어 데이터 베이스 액션을 수행할 수 없습니다.</string>
<string name="error_driver_required">%1$s 를 위한 드라이버가 요구됩니다.</string>
<string name="error_unable_merge_database_kdb">데이터베이스 V1로부터 병합할 수 없습니다.</string>
<string name="error_unable_merge_database_kdb">kdb 데이터베이스 파일과 병합할 수 없습니다.</string>
<string name="error_hardware_key_unsupported">하드웨어 키는 지원하지 않습니다.</string>
<string name="error_empty_key">키는 반드시 입력해야 합니다.</string>
<string name="hint_icon_name">아이콘명</string>
<string name="passphrase">암호문</string>
<string name="colorize_password_title">비밀번호에 색상 부여</string>
<string name="colorize_password_summary">타입에 따라 비밀번호 문자에 색상을 부여</string>
<string name="export_app_properties_title">속성을 내보내기</string>
<string name="export_app_properties_title">설정 내보내기</string>
<string name="uppercase">대문자</string>
<string name="menu_security_settings">보안 설정</string>
<string name="menu_reload_database">데이터 다시 읽기</string>
@@ -280,7 +279,7 @@
<string name="save_mode">저장 모드</string>
<string name="selection_mode">선택 모드</string>
<string name="remember_hardware_key_title">하드웨어 키를 기억</string>
<string name="export_app_properties_summary">속성을 내보 파일 생성</string>
<string name="export_app_properties_summary">설정을 내보내기 위해 파일 생성</string>
<string name="error_import_app_properties">앱 속성을 가져오던 중 오류 발생.</string>
<string name="contains_duplicate_uuid">데이터베이스가 중복된 UUID를 포함하고 있습니다.</string>
<string name="remember_database_locations_summary">데이터 베이스가 저장된 곳을 추적</string>
@@ -311,7 +310,7 @@
<string name="menu_restore_entry_history">이력을 복구</string>
<string name="subdomain_search_summary">보조 도메인 제한하에 웹 도메인을 검색</string>
<string name="contains_duplicate_uuid_procedure">중복에 대해 새로운 UUID를 생성하여 문제를 해결하고 진행할까요\?</string>
<string name="success_export_app_properties">속성을 내보냈습니다.</string>
<string name="success_export_app_properties">설정 내보냄</string>
<string name="parallelism">병렬 처리</string>
<string name="command_execution">명령 실행중…</string>
<string name="menu_master_key_settings">마스터 키 설정</string>
@@ -321,14 +320,14 @@
<string name="menu_device_unlock_settings">고급 잠금 해제</string>
<string name="search_mode">검색 모드</string>
<string name="rounds_explanation">추가적인 암호화 차수를 설정함으로써 무차별 대입 공격(brute force attack)에 대한 방어를 강화할 수 있습니다. 대신 읽기/저장시 느려질 수 있습니다.</string>
<string name="do_not_kill_app">앱을 강제 종료하지 마세요.</string>
<string name="do_not_kill_app">앱을 강제 종료하지 마세요</string>
<string name="sort_recycle_bin_bottom">휴지통을 바닥에</string>
<string name="warning_database_link_revoked">파일에 대한 접근이 파일 관리자에 의해 철회되었습니다.</string>
<string name="warning_database_link_revoked">파일에 대한 접근이 파일 관리자에 의해 철회되었습니다</string>
<string name="hide_broken_locations_title">파손된 데이터페이스 링크를 감춤</string>
<string name="import_app_properties_title">속성을 가져오기</string>
<string name="import_app_properties_title">설정 가져오기</string>
<string name="error_export_app_properties">앱 속성을 내보내던 중 오류 발생.</string>
<string name="rounds">변환 차수</string>
<string name="parallelism_explanation">키 파생 함수에 사용되는 병렬 처리 수준 (즉, 스레드의 갯수)</string>
<string name="parallelism_explanation">키 파생 함수에 사용되는 병렬 처리 수준(즉, 스레드의 갯수).</string>
<string name="html_about_privacy">&lt;strong&gt;사용자 데이터를 받아오지 않습니다.&lt;/strong&gt; 어떤 서버에도 연결하지 않고 로컬로만 동작하며 사용자의 사생활(프라이버시)를 최우선시합니다.</string>
<string name="error_rebuild_list">목록 재구축을 알맞게 할 수 없습니다.</string>
<string name="error_file_to_big">업로드하려는 파일이 너무 큽니다.</string>
@@ -339,50 +338,50 @@
<string name="show_otp_token_summary">항목 목록에서 OTP 토큰을 표시</string>
<string name="show_uuid_title">UUID를 표시</string>
<string name="show_uuid_summary">항목이나 그룹에 연결된 UUID를 표시</string>
<string name="creating_database">데이터 베이스 생성</string>
<string name="creating_database">데이터베이스 생성중…</string>
<string name="menu_merge_database">데이터 병합</string>
<string name="menu_merge_from">... 로부터 병합</string>
<string name="menu_save_copy_to">... 에 복사본 저장</string>
<string name="menu_keystore_remove_key">고급 잠금 해제 키 삭제</string>
<string name="menu_merge_from">로부터 병합</string>
<string name="menu_save_copy_to">에 복사본 저장</string>
<string name="menu_keystore_remove_key">디바이스 잠금 해제 키 삭제</string>
<string name="sort_last_access_time">접근</string>
<string name="invalid_db_same_uuid">%1$s 와 동일한 UUID %2$s 가 이미 존재합니다.</string>
<string name="underline">밑줄</string>
<string name="unsupported_db_version">지원하지 않는 데이터베이스 버전입니다.</string>
<string name="permission">허용</string>
<string name="permission">권한</string>
<string name="menu_appearance_settings">테마</string>
<string name="biometric">생체 인증</string>
<string name="device_credential">기기 자격 증명</string>
<string name="generate_keyfile">키 파일 생성하기</string>
<string name="menu_form_filling_settings_summary">키보드, 자동 완성, 클립보드</string>
<string name="menu_app_settings_summary">검색, 잠금, 기록, 속성</string>
<string name="merge_success">병합 성공습니다</string>
<string name="merge_success">병합 성공적으로 완료되었습니다</string>
<string name="version_label">버전 %1$s</string>
<string name="encrypted_value_stored">암호화된 비밀번호가 저장되었습니다</string>
<string name="info">정보</string>
<string name="build_label">빌드 %1$s</string>
<string name="configure_biometric">생체 인식 또는 장치 자격 증명이 등록되지 않았습니다.</string>
<string name="properties">속성</string>
<string name="menu_appearance_settings_summary">테마, 색상, 속성</string>
<string name="menu_appearance_settings_summary">테마, 색상, 아이콘, 글꼴, 속성</string>
<string name="autofill">자동 입력</string>
<string name="credential_before_click_device_unlock_button">비밀번호 입력 후 이 버튼을 눌러주세요</string>
<string name="unavailable">사용할 수 없습니다</string>
<string name="database_history">기록</string>
<string name="credential_before_click_device_unlock_button">비밀번호 입력 후 이 버튼을 눌러주세요.</string>
<string name="unavailable">사용 불가</string>
<string name="database_history">이력</string>
<string name="type">유형</string>
<string name="configure">설정</string>
<string name="biometric_security_update_required">등록된 생체정보를 업데이트해 주세요.</string>
<string name="autofill_select_entry">항목 선택하세요</string>
<string name="autofill_select_entry">항목 선택</string>
<string name="clipboard">클립보드</string>
<string name="clipboard_notifications_title">클립보드 알림</string>
<string name="lock_database_screen_off_summary">화면이 꺼지면 몇 초 후에 데이터베이스를 잠급니다.</string>
<string name="lock_database_screen_off_summary">화면이 꺼지면 몇 초 후에 데이터베이스를 잠급니다</string>
<string name="lock_database_back_root_title">\'­뒤로가기\'를 눌러 잠금</string>
<string name="unavailable_feature_text">이 기능을 사용할 수 없습니다.</string>
<string name="lock_database_show_button_summary">UI에 잠금버튼을 표시합니다</string>
<string name="lock_database_back_root_summary">사용자가 뒤로 가기 버튼을 누르면 데이터베이스를 잠급니다</string>
<string name="lock_database_back_root_summary">데이터베이스 루트 화면에 있는 경우 \'뒤로\' 버튼을 눌러 데이터베이스를 잠그십시오</string>
<string name="lock_database_show_button_title">잠금 버튼 표시</string>
<string name="unlock">잠금 해제</string>
<string name="lock">잠금</string>
<string name="autofill_sign_in_prompt">KeePassDX로 로그인</string>
<string name="password_size_title">생성된 비밀번호 크기</string>
<string name="password_size_title">생성된 패스워드 길이</string>
<string name="autofill_preference_title">자동 완성 설정</string>
<string name="set_credential_provider_service_title">기본 자동완성 서비스 설정</string>
<string name="lock_database_screen_off_title">화면 꺼짐 시 잠금</string>

View File

@@ -475,7 +475,7 @@
<string name="autofill_ask_to_save_data_summary">Запрашивать сохранение данных после завершения заполнения формы</string>
<string name="autofill_ask_to_save_data_title">Запрос сохранения данных</string>
<string name="autofill_close_database_summary">Закрывать базу после выбора автозаполнения</string>
<string name="autofill_close_database_title">Закрыть базу</string>
<string name="autofill_close_database_title">Закрывать базу</string>
<string name="notification">Уведомление</string>
<string name="biometric_security_update_required">Требуется обновление биометрической системы безопасности.</string>
<string name="warning_empty_recycle_bin">Удалить всё содержимое корзины безвозвратно\?</string>
@@ -585,7 +585,7 @@
<string name="show_otp_token_summary">Показывать токены OTP в списке записей</string>
<string name="menu_external_icon">Внешний значок</string>
<string name="autofill_select_entry">Выбор записи…</string>
<string name="autofill_manual_selection_summary">Показывать функцию, позволяющую пользователю вручную выбирать запись из базы</string>
<string name="autofill_manual_selection_summary">Показывать элемент управления для ручного выбора пользователем записи из базы</string>
<string name="autofill_manual_selection_title">Ручной выбор</string>
<string name="hint_icon_name">Название значка</string>
<string name="permission">Разрешение</string>

View File

@@ -569,7 +569,7 @@
<string name="icon_pack_choose_summary">Paketë ikonash të përdorura te aplikacioni</string>
<string name="hide_expired_entries_summary">Zërat e skaduar nuk shfaqen</string>
<string name="set_credential_provider_service_title">Caktoni shërbim parazgjedhje vetëplotësimesh</string>
<string name="autofill_explanation_summary">Aktivizoni vetëplotësimet, për të plotësuar shpejt formularë në aplikacione të tjerë</string>
<string name="autofill_explanation_summary">Formësoni vetëplotësimet, për të plotësuar shpejt formularë në aplikacione të tjerë</string>
<string name="autofill_preference_title">Rregullime vetëplotësimi</string>
<string name="password_size_summary">Cakton madhësinë parazgjedhje për fjalëkalimet e prodhuar</string>
<string name="list_password_generator_options_summary">Caktoni shenja të lejuara për prodhuesin e fjalëkalimeve</string>
@@ -623,14 +623,14 @@
<string name="education_field_copy_summary">Çkopjohet te fusha, mund të ngjitet kudo.\n\nPërdorni metodën mbushje formularësh, nëse parapëlqeni.</string>
<string name="education_donation_summary">Ndihmoni të shtohet qëndrueshmëria, siguria dhe në shtimin e më tepër veçorive.</string>
<string name="html_text_ad_free">Ndryshe nga shumë aplikacione administrimi fjalëkalimesh, ky është &lt;strong&gt;pa reklama&lt;/strong&gt;, &lt;strong&gt;<em>software</em> i lirë në <em>copylef</em>&lt;/strong&gt; dhe sgrumbullon të dhëna personale në shërbyesit e tij, pavarësisht versionit që përdorni.</string>
<string name="html_text_buy_pro">Duke blerë versionin Pro, do të mund të përdorni këtë &lt;strong&gt;stil pamor&lt;/strong&gt; dhe do të ndihmoni veçanërisht në &lt;strong&gt;realizimin e projekteve të bashkësisë.&lt;strong&gt;</string>
<string name="html_text_buy_pro">Duke blerë versionin Pro, do të mund të përdorni këtë &lt;strong&gt;stil pamor&lt;/strong&gt; dhe do të ndihmoni veçanërisht në &lt;strong&gt;realizimin e projekteve të bashkësisë.&lt;/strong&gt;</string>
<string name="error_arc4">Smbulohet shifër “Arcfour stream”.</string>
<string name="warning_file_too_big">Një bazë të dhënash KeePass supozohet se përmban vetëm kartela të vockla mjetesh (b.f., kartela kyçesh PGP).\n\nMe këtë ngarkim baza juaj e të dhënave mund të bëhet shumë e madhe dhe të ulet funksionimi i saj.</string>
<string name="education_read_only_summary">Ndryshoni mënyrë hapjeje për këtë sesion. \n \n“Mbrojtur nga shkimi” parandalon ndryshime të paqëllimta te baza e të dhënave. \n“E ndryshueshme” ju lejon të shtoni, fshini ose ndryshoni krejt elementët, sipas dëshirës.</string>
<string name="html_text_dev_feature_encourage">po i nxisni zhvilluesit të krijojnë &lt;strong&gt;veçori të reja&lt;/strong&gt; dhe të &lt;strong&gt;ndreqin të meta&lt;/strong&gt; në përputhje me sugjerimet tuaja.</string>
<string name="warning_large_keyfile">Nuk rekomandohet të shtohet një kartelë kyç e madhe, kjo mund të pengojë hapjen e bazës së të dhënave.</string>
<string name="education_entry_edit_summary">Përpunojeni zërin tuaj me fusha vetjake. Pool data can be referenced between different entry fields.</string>
<string name="error_otp_type">Lloji OTP ekzistues snjihet nga ky formular, vlerësimi i tij mund të mos prodhojë më saktë token-in.</string>
<string name="error_otp_type">Lloji OTP ekzistues snjihet nga ky formular dhe vlerësimi i tij mund të mos prodhojë më saktë token-in.</string>
<string name="warning_database_read_only">Akordo hyrje për shkrim kartelash, që të ruhen ndryshimet e bazës së të dhënave</string>
<string name="warning_database_info_changed_options">Përzieni të dhënat, mbishkruani ndryshimet që nga jashtë duke e ruajtur bazën e të dhënave, ose duke e ringarkuar me ndryshimet më të reja.</string>
<string name="warning_database_revoked">Hyrja te kartela e shfuqizuar nga përgjegjësi i kartelave, mbylleni bazën e të dhënave dhe rihapeni që prej vendndodhjes së saj.</string>

View File

@@ -433,6 +433,32 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
}
}
/**
* Return the real parent in database from a group defined as parent
* If the parent is null, simply return the root group
* Guaranteed that a return group is linked to the database tree through its ancestors
*/
private fun getAttachedParent(
group: GroupKDBX?
): GroupKDBX {
var realParent: GroupKDBX = database.rootGroup!!
group?.let { parent ->
val parentInDatabase = database.getGroupById(parent.nodeId)
if (parentInDatabase == null) {
realParent = GroupKDBX().apply {
updateWith(parent, updateParents = false)
}
database.addGroupTo(
realParent,
getAttachedParent(parent.parent)
)
} else {
realParent = parentInDatabase
}
}
return realParent
}
/**
* Utility method to merge a KDBX entry
*/
@@ -443,10 +469,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
databaseToMerge.getEntryById(entryId)?.let { srcEntryToMerge ->
// Retrieve parent in current database
var parentEntryToMerge: GroupKDBX? = null
srcEntryToMerge.parent?.nodeId?.let {
parentEntryToMerge = database.getGroupById(it)
}
val parentEntryToMerge: GroupKDBX = getAttachedParent(srcEntryToMerge.parent)
val entryToMerge = EntryKDBX().apply {
updateWith(srcEntryToMerge, copyHistory = true, updateParents = false)
}
@@ -478,8 +501,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
// If it's a deleted object, but another instance was updated
// If entry parent to add exists and in current database
if ((deletedObject == null
|| deletedObject.deletionTime.isBefore(entryToMerge.lastModificationTime))
&& parentEntryToMerge != null) {
|| deletedObject.deletionTime.isBefore(entryToMerge.lastModificationTime))) {
database.addEntryTo(entryToMerge, parentEntryToMerge)
}
} else {
@@ -488,51 +510,46 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
// Merge by modification time
if (entry.lastModificationTime.isBefore(entryToMerge.lastModificationTime)) {
// Update entry with databaseEntryToMerge and merge history
addHistory(entry, entryToMerge)
if (parentEntryToMerge == entry.parent) {
entry.updateWith(entryToMerge, copyHistory = true, updateParents = false)
} else {
database.removeEntryFrom(entry, entry.parent)
if (parentEntryToMerge != null) {
database.addEntryTo(entryToMerge, parentEntryToMerge)
}
}
entryToMerge.addHistoryFrom(entry)
entry.updateWith(entryToMerge, copyHistory = true, updateParents = false)
// Move the current entry to the verified location
database.removeEntryFrom(entry, entry.parent)
database.addEntryTo(entry, parentEntryToMerge)
} else if (entry.lastModificationTime.isAfter(entryToMerge.lastModificationTime)) {
addHistory(entryToMerge, entry)
// Don't touch the location but update the entry history
entry.addHistoryFrom(entryToMerge)
} else if (entry.lastModificationTime.isEquals(entryToMerge.lastModificationTime)) {
// If it's the same modification time, simply move entry to the right location
parentEntryToMerge?.let {
database.removeEntryFrom(entry, entry.parent)
database.addEntryTo(entryToMerge, parentEntryToMerge)
}
// If it's the same modification time, simply move entry to the right location,
// Current entry and entry to merge are normally the same
database.removeEntryFrom(entry, entry.parent)
database.addEntryTo(entry, parentEntryToMerge)
}
}
}
}
/**
* Utility method to merge an history from an [entryA] to an [entryB],
* [entryB] is modified
* Utility method to merge an history from an [entryA]
*/
private fun addHistory(entryA: EntryKDBX, entryB: EntryKDBX) {
private fun EntryKDBX.addHistoryFrom(entryA: EntryKDBX) {
// Keep entry as history if already not present
entryA.history.forEach { history ->
// If history not present
if (!entryB.history.any {
if (!this.history.any {
it.lastModificationTime == history.lastModificationTime
}) {
entryB.addEntryToHistory(history)
this.addEntryToHistory(history)
}
}
// Last entry not present
if (entryB.history.find {
if (this.history.find {
it.lastModificationTime == entryA.lastModificationTime
} == null) {
val history = EntryKDBX().apply {
updateWith(entryA, copyHistory = false, updateParents = false)
parent = null
}
entryB.addEntryToHistory(history)
this.addEntryToHistory(history)
}
}
@@ -546,10 +563,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
databaseToMerge.getGroupById(groupId)?.let { srcGroupToMerge ->
// Retrieve parent in current database
var parentGroupToMerge: GroupKDBX? = null
srcGroupToMerge.parent?.nodeId?.let {
parentGroupToMerge = database.getGroupById(it)
}
val parentGroupToMerge: GroupKDBX = getAttachedParent(srcGroupToMerge.parent)
val groupToMerge = GroupKDBX().apply {
updateWith(srcGroupToMerge, updateParents = false)
}
@@ -557,8 +571,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
if (group == null) {
// If group parent to add exists and in current database
if ((deletedObject == null
|| deletedObject.deletionTime.isBefore(groupToMerge.lastModificationTime))
&& parentGroupToMerge != null) {
|| deletedObject.deletionTime.isBefore(groupToMerge.lastModificationTime))) {
database.addGroupTo(groupToMerge, parentGroupToMerge)
}
} else {
@@ -566,20 +579,16 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) {
mergeCustomData(group.customData, groupToMerge.customData)
// Merge by modification time
if (group.lastModificationTime.isBefore(groupToMerge.lastModificationTime)) {
if (parentGroupToMerge == group.parent) {
group.updateWith(groupToMerge, false)
} else {
database.removeGroupFrom(group, group.parent)
if (parentGroupToMerge != null) {
database.addGroupTo(groupToMerge, parentGroupToMerge)
}
}
group.updateWith(groupToMerge, updateParents = false)
// Update the current group location to the verified one
database.removeGroupFrom(group, group.parent)
database.addGroupTo(group, parentGroupToMerge)
} else if (group.lastModificationTime.isAfter(group.lastModificationTime)) {
// Don't touch the location
} else if (group.lastModificationTime.isEquals(groupToMerge.lastModificationTime)) {
// If it's the same modification time, simply move group to the right location
parentGroupToMerge?.let {
database.removeGroupFrom(group, group.parent)
database.addGroupTo(groupToMerge, parentGroupToMerge)
}
database.removeGroupFrom(group, group.parent)
database.addGroupTo(group, parentGroupToMerge)
}
}
}

View File

@@ -71,7 +71,7 @@ object AppOriginEntryField {
/**
* Useful to detect if an other KeePass compatibility app already add a web domain or an app id
*/
private fun EntryInfo.containsDomainOrApplicationId(search: String): Boolean {
fun EntryInfo.containsDomainOrApplicationId(search: String): Boolean {
if (url.contains(search))
return true
return customFields.find {
@@ -159,5 +159,7 @@ object AppOriginEntryField {
*/
fun Field.isWebDomain(): Boolean {
return this.name.startsWith(WEB_DOMAIN_FIELD_NAME)
|| this.name.contains("_$WEB_DOMAIN_FIELD_NAME")
|| this.name.contains("${WEB_DOMAIN_FIELD_NAME}_")
}
}

View File

@@ -27,6 +27,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Field
import com.kunzisoft.keepass.database.element.Tags
import com.kunzisoft.keepass.database.element.entry.AutoType
import com.kunzisoft.keepass.model.AppOriginEntryField.containsDomainOrApplicationId
import com.kunzisoft.keepass.model.AppOriginEntryField.setAppOrigin
import com.kunzisoft.keepass.model.AppOriginEntryField.setApplicationId
import com.kunzisoft.keepass.model.AppOriginEntryField.setWebDomain
@@ -183,6 +184,18 @@ class EntryInfo : NodeInfo {
}
}
/**
* True if this entry contains domain or applicationId,
* OTP is ignored and considered not present
*/
fun containsSearchInfo(searchInfo: SearchInfo): Boolean {
return searchInfo.webDomain?.let { webDomain ->
containsDomainOrApplicationId(webDomain)
} ?: searchInfo.applicationId?.let { applicationId ->
containsDomainOrApplicationId(applicationId)
} ?: false
}
/**
* Add searchInfo to current EntryInfo
*/

View File

@@ -1 +1 @@
KeePassDX مدير كلمات سر مفتوح المصدر
KeePassDX

View File

@@ -0,0 +1,4 @@
* Fix database merge algorithm #2223
* Fix save search info #2243
* Fix Play Service as privileged app for Passkey Cross Device Authentication #2244
* Small fixes

View File

@@ -0,0 +1,4 @@
* Fix multiple Passkey selection #2253
* Fix database dialog subtitle #2254
* Fix save search info if URL present #2255
* Small fixes

View File

@@ -1 +1 @@
KeePassDX - Contraseñas FOSS
KeePassDX Pass(key/word) Vault

View File

@@ -0,0 +1,4 @@
* Correction de l'algorithme de fusion des bases de données #2223
* Correction de la sauvegarde des infos de recherche #2243
* Correction Play Service comme appli privilégiée pour l'authentification Passkey multi-appareils #2244
* Small fixes

View File

@@ -0,0 +1,4 @@
* Correction de la selection multiple des Passkeys #2253
* Correction du sous-titre du dialogue de la base de données #2254
* Correction de la sauvegarde des infos de recherchesi l'URL est present #2255
* Petites corrections

View File

@@ -1 +1 @@
KeePassDX FOSS trezor za lozinke
KeePassDX Pass(key/word) Vault

View File

@@ -1 +1 @@
KeePassDX Pengaman Sandi FOSS
KeePassDX Pass(key/word) Vault

View File

@@ -1 +1 @@
Cassaforte di pass(key/word) KeePassDX
KeePassDX Pass(key/word) Vault

View File

@@ -1 +1 @@
KeePassDX - Безбедносен софтвер за лозинки и е слободен софтвер
KeePassDX Pass(key/word) Vault

View File

@@ -1 +1 @@
KeePassDX - менеджер паролей/ключей
KeePassDX - менеджер паролей

View File

@@ -1 +1 @@
KeePassDX - Parola/Anahtar Kasası
KeePassDX Pass(key/word) Vault

View File

@@ -1 +1 @@
KeePassDX - FOSS 密碼管理器
KeePassDX Pass(key/word) Vault