From 28e4d929bb6879a2d2871c4a443764610f22c8df Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 9 Sep 2025 20:55:30 +0200 Subject: [PATCH 1/9] fix: Warnings --- .../keepass/services/DatabaseTaskNotificationService.kt | 2 +- app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt index 9d6c0baf3..4cd6ee6da 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt @@ -218,7 +218,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress Log.i(TAG, "Database file modified " + "$previousDatabaseInfo != $lastFileDatabaseInfo ") // Call listener to indicate a change in database info - if (!mSaveState && previousDatabaseInfo != null) { + if (!mSaveState) { mDatabaseInfoListeners.forEach { listener -> listener.onDatabaseInfoChanged( previousDatabaseInfo, diff --git a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt index 1ada1c3b9..1768c3c49 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/ViewUtil.kt @@ -304,6 +304,7 @@ fun CollapsingToolbarLayout.changeTitleColor(color: Int) { invalidate() } +@Suppress("DEPRECATION") fun Activity.setTransparentNavigationBar(applyToStatusBar: Boolean = false, applyWindowInsets: () -> Unit) { // Only in portrait if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 From 3ee4caa153f44860c08e9a2565e1ba1a77d5e9df Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 11 Sep 2025 14:53:41 +0200 Subject: [PATCH 2/9] fix: Warnings --- .../src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt b/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt index 15b172db3..a7676cf51 100644 --- a/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt +++ b/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt @@ -92,7 +92,7 @@ inline fun Parcel.readParcelableCompat(): T? = when { fun Parcel.readParcelableCompat(clazz: Class): T? = when { SDK_INT >= 33 -> readParcelable(clazz.classLoader, clazz) - else -> @Suppress("DEPRECATION") readParcelable(clazz.classLoader) as? T + else -> @Suppress("DEPRECATION", "UNCHECKED_CAST") (readParcelable(clazz.classLoader) as? T) } inline fun Parcel.readSerializableCompat(): T? = when { From 7212c73481edce4416c13338459e5eb2d041406d Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 11 Sep 2025 14:55:37 +0200 Subject: [PATCH 3/9] fix: Warnings --- .../com/kunzisoft/keepass/utils/ParcelableUtil.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt b/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt index a7676cf51..fa49bf6ab 100644 --- a/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt +++ b/database/src/main/java/com/kunzisoft/keepass/utils/ParcelableUtil.kt @@ -120,19 +120,19 @@ fun Parcel.writeParcelableMap(map: Map, f inline fun Parcel.readParcelableMap(): Map { val size = readInt() val map = HashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: K? = try { when { SDK_INT >= 33 -> readParcelable(K::class.java.classLoader, K::class.java) else -> @Suppress("DEPRECATION") readParcelable(K::class.java.classLoader) } - } catch (e: Exception) { null } + } catch (_: Exception) { null } val value: V? = try { when { SDK_INT >= 33 -> readParcelable(V::class.java.classLoader, V::class.java) else -> @Suppress("DEPRECATION") readParcelable(V::class.java.classLoader) } - } catch (e: Exception) { null } + } catch (_: Exception) { null } if (key != null && value != null) map[key] = value } @@ -152,14 +152,14 @@ fun Parcel.writeStringParcelableMap(map: HashMap, fl inline fun Parcel.readStringParcelableMap(): LinkedHashMap { val size = readInt() val map = LinkedHashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: String? = readString() val value: V? = try { when { SDK_INT >= 33 -> readParcelable(V::class.java.classLoader, V::class.java) else -> @Suppress("DEPRECATION") readParcelable(V::class.java.classLoader) } - } catch (e: Exception) { null } + } catch (_: Exception) { null } if (key != null && value != null) map[key] = value } @@ -179,7 +179,7 @@ fun Parcel.writeStringIntMap(map: LinkedHashMap) { fun Parcel.readStringIntMap(): LinkedHashMap { val size = readInt() val map = LinkedHashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: String? = readString() val value: Int = readInt() if (key != null) @@ -201,7 +201,7 @@ fun Parcel.writeStringStringMap(map: MutableMap) { fun Parcel.readStringStringMap(): LinkedHashMap { val size = readInt() val map = LinkedHashMap(size) - for (i in 0 until size) { + (0 until size).forEach { i -> val key: String? = readString() val value: String? = readString() if (key != null && value != null) From 39d9a74a737ebf6b0a7a0a92b1618a753151a4c5 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 11 Sep 2025 16:36:35 +0200 Subject: [PATCH 4/9] fix: Warnings --- .../keepass/services/DatabaseTaskNotificationService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt index 4cd6ee6da..9d6c0baf3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/DatabaseTaskNotificationService.kt @@ -218,7 +218,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress Log.i(TAG, "Database file modified " + "$previousDatabaseInfo != $lastFileDatabaseInfo ") // Call listener to indicate a change in database info - if (!mSaveState) { + if (!mSaveState && previousDatabaseInfo != null) { mDatabaseInfoListeners.forEach { listener -> listener.onDatabaseInfoChanged( previousDatabaseInfo, From 69e7cdbc4715fa609ffa13d1c7396236b3c80f5f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 11 Sep 2025 16:43:40 +0200 Subject: [PATCH 5/9] fix: Search with space #175 --- .../keepass/database/helper/SearchHelper.kt | 6 +++--- .../keepass/database/search/SearchHelper.kt | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt b/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt index c4a90cf3c..232b88e17 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/helper/SearchHelper.kt @@ -59,9 +59,9 @@ object SearchHelper { && !searchInfo.containsOnlyNullValues()) { // If search provide results database.createVirtualGroupFromSearchInfo( - searchInfo.toString(), - searchInfo.isASearchByDomain(), - MAX_SEARCH_ENTRY + searchInfoString = searchInfo.toString(), + searchInfoByDomain = searchInfo.isASearchByDomain(), + max = MAX_SEARCH_ENTRY )?.let { searchGroup -> if (searchGroup.numberOfChildEntries > 0) { searchWithoutUI = true diff --git a/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt b/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt index f8f3da992..3be886cd1 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/search/SearchHelper.kt @@ -151,7 +151,7 @@ class SearchHelper { if (searchParameters.searchByDomain) { try { stringToCheck.inTheSameDomainAs(word, sameSubDomain = true) - } catch (e: Exception) { + } catch (_: Exception) { false } } else null @@ -204,10 +204,18 @@ class SearchHelper { regex.matches(stringToCheck) } else { specialComparison?.invoke(stringToCheck, searchParameters.searchQuery) - ?: stringToCheck.contains( - searchParameters.searchQuery, - !searchParameters.caseSensitive - ) + ?: run { + // Search with space separator #175 + var searchFound = true + searchParameters.searchQuery.split(" ").forEach { word -> + searchFound = searchFound + && stringToCheck.contains( + word, + !searchParameters.caseSensitive + ) + } + searchFound + } } } } From 2fc2a9c7c132dbf861107e0b16f5dfc06b848337 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 11 Sep 2025 21:19:40 +0200 Subject: [PATCH 6/9] fix: Delete algo during merge #1516 --- .../database/merge/DatabaseKDBXMerger.kt | 131 ++++++++++++++---- 1 file changed, 107 insertions(+), 24 deletions(-) diff --git a/database/src/main/java/com/kunzisoft/keepass/database/merge/DatabaseKDBXMerger.kt b/database/src/main/java/com/kunzisoft/keepass/database/merge/DatabaseKDBXMerger.kt index 1f83169ee..19e18b9e6 100644 --- a/database/src/main/java/com/kunzisoft/keepass/database/merge/DatabaseKDBXMerger.kt +++ b/database/src/main/java/com/kunzisoft/keepass/database/merge/DatabaseKDBXMerger.kt @@ -22,6 +22,7 @@ package com.kunzisoft.keepass.database.merge import com.kunzisoft.keepass.database.element.Attachment import com.kunzisoft.keepass.database.element.CustomData import com.kunzisoft.keepass.database.element.DateInstant +import com.kunzisoft.keepass.database.element.DeletedObject import com.kunzisoft.keepass.database.element.database.DatabaseKDB import com.kunzisoft.keepass.database.element.database.DatabaseKDBX import com.kunzisoft.keepass.database.element.entry.EntryKDB @@ -32,9 +33,10 @@ import com.kunzisoft.keepass.database.element.node.NodeHandler import com.kunzisoft.keepass.database.element.node.NodeId 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.utils.readAllBytes import java.io.IOException -import java.util.* +import java.util.UUID class DatabaseKDBXMerger(private var database: DatabaseKDBX) { @@ -180,7 +182,7 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) { } /** - * Merge a KDB> database in a KDBX database, + * Merge a KDBX database in a KDBX database, * Try to take into account the modification date of each element * To make a merge as accurate as possible */ @@ -302,32 +304,113 @@ class DatabaseKDBXMerger(private var database: DatabaseKDBX) { } // Manage deleted objects - databaseToMerge.deletedObjects.forEach { deletedObject -> - val deletedObjectId = deletedObject.uuid - val databaseEntry = database.getEntryById(deletedObjectId) - val databaseGroup = database.getGroupById(deletedObjectId) - val databaseIcon = database.iconsManager.getIcon(deletedObjectId) - val databaseIconModificationTime = databaseIcon?.lastModificationTime - if (databaseEntry != null - && deletedObject.deletionTime.isAfter(databaseEntry.lastModificationTime)) { - database.removeEntryFrom(databaseEntry, databaseEntry.parent) - } - if (databaseGroup != null - && deletedObject.deletionTime.isAfter(databaseGroup.lastModificationTime)) { - database.removeGroupFrom(databaseGroup, databaseGroup.parent) - } - if (databaseIcon != null - && ( - databaseIconModificationTime == null - || (deletedObject.deletionTime.isAfter(databaseIconModificationTime)) - ) - ) { - database.removeCustomIcon(deletedObjectId) - } + val deletedObjects = databaseToMerge.deletedObjects + deletedObjects.forEach { deletedObject -> + deleteEntry(deletedObject) + deleteGroup(deletedObject, deletedObjects) + deleteIcon(deletedObject) // Attachments are removed and optimized during the database save } } + /** + * Delete an entry from the database with the [deletedEntry] id + */ + private fun deleteEntry(deletedEntry: DeletedObject) { + val databaseEntry = database.getEntryById(deletedEntry.uuid) + if (databaseEntry != null + && deletedEntry.deletionTime.isAfter(databaseEntry.lastModificationTime)) { + database.removeEntryFrom(databaseEntry, databaseEntry.parent) + } + } + + /** + * Check whether a node is in the list of deleted objects + */ + private fun Set.containsNode(node: NodeVersioned): Boolean { + return this.any { it.uuid == node.nodeId.id } + } + + /** + * Check whether a node is not in the list of deleted objects + */ + private fun Set.notContainsNode(node: NodeVersioned): Boolean { + return !this.containsNode(node) + } + + /** + * Get the first parent not deleted + */ + private fun firstNotDeletedParent( + node: NodeVersioned, + deletedObjects: Set + ): GroupKDBX? { + var parent = node.parent + while (parent != null && deletedObjects.containsNode(parent)) { + parent = node.parent + } + return parent + } + + /** + * Delete a group from the database with the [deletedGroup] id + * Recursively check whether a group to be deleted contains a node not to be deleted with [deletedObjects] + * and move it to the first parent that has not been deleted. + */ + private fun deleteGroup(deletedGroup: DeletedObject, deletedObjects: Set) { + val databaseGroup = database.getGroupById(deletedGroup.uuid) + if (databaseGroup != null + && deletedGroup.deletionTime.isAfter(databaseGroup.lastModificationTime)) { + // Must be in dedicated list to prevent modification collision + val entriesToMove = mutableListOf() + databaseGroup.getChildEntries().forEach { child -> + // If the child entry is not a deleted object, + if (deletedObjects.notContainsNode(child)) { + entriesToMove.add(child) + } + } + val groupsToMove = mutableListOf() + databaseGroup.getChildGroups().forEach { child -> + // Move the group to the first parent not deleted + // the deleted objects will take care of remove it later + groupsToMove.add(child) + } + // For each node to move, move it + // try to move the child entry in the first parent not deleted + entriesToMove.forEach { child -> + database.removeEntryFrom(child, child.parent) + database.addEntryTo( + child, + firstNotDeletedParent(databaseGroup, deletedObjects) + ) + } + groupsToMove.forEach { child -> + database.removeGroupFrom(child, child.parent) + database.addGroupTo( + child, + firstNotDeletedParent(databaseGroup, deletedObjects) + ) + } + // Then delete the group + database.removeGroupFrom(databaseGroup, databaseGroup.parent) + } + } + + /** + * Delete an icon from the database with the [deletedIcon] id + */ + private fun deleteIcon(deletedIcon: DeletedObject) { + val deletedObjectId = deletedIcon.uuid + val databaseIcon = database.iconsManager.getIcon(deletedObjectId) + val databaseIconModificationTime = databaseIcon?.lastModificationTime + if (databaseIcon != null + && (databaseIconModificationTime == null + || (deletedIcon.deletionTime.isAfter(databaseIconModificationTime))) + ) { + database.removeCustomIcon(deletedObjectId) + } + } + /** * Merge [customDataToMerge] in [customData] */ From 56cb5953dde4ed3ce373b34ec152b8a857cb8a54 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 12 Sep 2025 13:00:56 +0200 Subject: [PATCH 7/9] fix: Deletable recycle bin #2163 --- .../activities/dialogs/SortDialogFragment.kt | 15 ++------ .../activities/fragments/GroupFragment.kt | 38 +++++++------------ 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt index 45cbb9db1..78972aa76 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/SortDialogFragment.kt @@ -176,21 +176,14 @@ class SortDialogFragment : DatabaseDialogFragment() { return bundle } - fun getInstance(sortNodeEnum: SortNodeEnum, - ascending: Boolean, - groupsBefore: Boolean): SortDialogFragment { - val bundle = buildBundle(sortNodeEnum, ascending, groupsBefore) - val fragment = SortDialogFragment() - fragment.arguments = bundle - return fragment - } - fun getInstance(sortNodeEnum: SortNodeEnum, ascending: Boolean, groupsBefore: Boolean, - recycleBinBottom: Boolean): SortDialogFragment { + recycleBinBottom: Boolean?): SortDialogFragment { val bundle = buildBundle(sortNodeEnum, ascending, groupsBefore) - bundle.putBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY, recycleBinBottom) + recycleBinBottom?.let { + bundle.putBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY, recycleBinBottom) + } val fragment = SortDialogFragment() fragment.arguments = bundle return fragment diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt index 7a7cf5acf..f26cc3a70 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/fragments/GroupFragment.kt @@ -76,9 +76,6 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen private var specialMode: SpecialMode = SpecialMode.DEFAULT - private var mRecycleBinEnable: Boolean = false - private var mRecycleBin: Group? = null - private var mRecycleViewScrollListener = object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) @@ -102,21 +99,14 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen R.id.menu_sort -> { context?.let { context -> val sortDialogFragment: SortDialogFragment = - if (mRecycleBinEnable) { - SortDialogFragment.getInstance( - PreferencesUtil.getListSort(context), - PreferencesUtil.getAscendingSort(context), - PreferencesUtil.getGroupsBeforeSort(context), + SortDialogFragment.getInstance( + PreferencesUtil.getListSort(context), + PreferencesUtil.getAscendingSort(context), + PreferencesUtil.getGroupsBeforeSort(context), + if (mDatabase?.isRecycleBinEnabled == true) { PreferencesUtil.getRecycleBinBottomSort(context) - ) - } else { - SortDialogFragment.getInstance( - PreferencesUtil.getListSort(context), - PreferencesUtil.getAscendingSort(context), - PreferencesUtil.getGroupsBeforeSort(context) - ) - } - + } else null + ) sortDialogFragment.show(childFragmentManager, "sortDialog") } true @@ -165,9 +155,6 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen } override fun onDatabaseRetrieved(database: ContextualDatabase?) { - mRecycleBinEnable = database?.isRecycleBinEnabled == true - mRecycleBin = database?.recycleBin - context?.let { context -> database?.let { database -> mAdapter = NodesAdapter(context, database).apply { @@ -312,6 +299,11 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen } } + private fun containsRecycleBin(nodes: List): Boolean { + return mDatabase?.isRecycleBinEnabled == true + && nodes.any { it == mDatabase?.recycleBin } + } + fun actionNodesCallback(database: ContextualDatabase, nodes: List, menuListener: NodesActionMenuListener?, @@ -336,8 +328,7 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen // Open and Edit for a single item if (nodes.size == 1) { // Edition - if (database.isReadOnly - || (mRecycleBinEnable && nodes[0] == mRecycleBin)) { + if (database.isReadOnly || containsRecycleBin(nodes)) { menu?.removeItem(R.id.menu_edit) } } else { @@ -357,8 +348,7 @@ class GroupFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListen } // Deletion - if (database.isReadOnly - || (mRecycleBinEnable && nodes.any { it == mRecycleBin })) { + if (database.isReadOnly || containsRecycleBin(nodes)) { menu?.removeItem(R.id.menu_delete) } } From af068349e45733594a1b669b29489c60b2afb98d Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 12 Sep 2025 14:14:06 +0200 Subject: [PATCH 8/9] fix: Upgrade to 4.1.8 --- CHANGELOG | 8 ++++++++ app/build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/141.txt | 6 ++++++ fastlane/metadata/android/fr-FR/changelogs/141.txt | 6 ++++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/141.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/141.txt diff --git a/CHANGELOG b/CHANGELOG index 6acc3f201..23aeb1c88 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +KeePassDX(4.1.8) + * Updated to API 35 minimum SDK 19 #2073 #2138 #2067 #2133 #1687 (Thx @Dev-ClayP) + * Remember last read-only state #2099 #2100 (Thx @rmacklin) + * Fix merge deletion #1516 + * Fix space in search #175 + * Fix deletable recycle bin #2163 + * Small fixes + KeePassDX(4.1.7) * Fix CipherDatabase for biometric states #2119 diff --git a/app/build.gradle b/app/build.gradle index 4b5287f6a..090ae8234 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 19 targetSdkVersion 35 - versionCode = 139 - versionName = "4.1.7" + versionCode = 141 + versionName = "4.1.8" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" diff --git a/fastlane/metadata/android/en-US/changelogs/141.txt b/fastlane/metadata/android/en-US/changelogs/141.txt new file mode 100644 index 000000000..7316a4d1f --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/141.txt @@ -0,0 +1,6 @@ + * Updated to API 35 minimum SDK 19 #2073 #2138 #2067 #2133 #1687 (Thx @Dev-ClayP) + * Remember last read-only state #2099 #2100 (Thx @rmacklin) + * Fix merge deletion #1516 + * Fix space in search #175 + * Fix deletable recycle bin #2163 + * Small fixes \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/141.txt b/fastlane/metadata/android/fr-FR/changelogs/141.txt new file mode 100644 index 000000000..c89f4045d --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/141.txt @@ -0,0 +1,6 @@ + * Mise à jour vers API 35 minimum SDK 19 #2073 #2138 #2067 #2133 #1687 (Thx @Dev-ClayP) + * Sauvegarde du dernier état lecture seule #2099 #2100 (Thx @rmacklin) + * Correction de la suppression lors d'un merge #1516 + * Correction des espaces dans la recherche #175 + * Correction de la poubelle supprimable #2163 + * Petites corrections \ No newline at end of file From da8ef9340cf4f96dda5f9277e2c22149576e259e Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 12 Sep 2025 15:23:32 +0200 Subject: [PATCH 9/9] fix: Loading ViewModel --- .../activities/MainCredentialActivity.kt | 67 ++++++++++--------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt index cd45b54a3..4bca99154 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/MainCredentialActivity.kt @@ -43,6 +43,7 @@ import androidx.biometric.BiometricManager import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.commit import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.snackbar.Snackbar @@ -75,8 +76,8 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion. import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.MAIN_CREDENTIAL_KEY import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.READ_ONLY_KEY -import com.kunzisoft.keepass.settings.DeviceUnlockSettingsActivity import com.kunzisoft.keepass.settings.AppearanceSettingsActivity +import com.kunzisoft.keepass.settings.DeviceUnlockSettingsActivity import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION @@ -107,7 +108,11 @@ class MainCredentialActivity : DatabaseModeActivity() { private var deviceUnlockFragment: DeviceUnlockFragment? = null private val mDatabaseFileViewModel: DatabaseFileViewModel by viewModels() - private val mDeviceUnlockViewModel: DeviceUnlockViewModel by viewModels() + private val mDeviceUnlockViewModel: DeviceUnlockViewModel? by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ViewModelProvider(this)[DeviceUnlockViewModel::class.java] + } else null + } private val mPasswordActivityEducation = PasswordActivityEducation(this) @@ -177,7 +182,7 @@ class MainCredentialActivity : DatabaseModeActivity() { // Listen password checkbox to init advanced unlock and confirmation button mainCredentialView?.onConditionToStoreCredentialChanged = { _, verified -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.checkConditionToStoreCredential( + mDeviceUnlockViewModel?.checkConditionToStoreCredential( condition = verified ) } @@ -242,29 +247,31 @@ class MainCredentialActivity : DatabaseModeActivity() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.uiState.collect { uiState -> - // New value received - uiState.credentialRequiredCipher?.let { cipher -> - mDeviceUnlockViewModel.encryptCredential( - credential = getCredentialForEncryption(), - cipher = cipher - ) - } - uiState.cipherEncryptDatabase?.let { cipherEncryptDatabase -> - onCredentialEncrypted(cipherEncryptDatabase) - mDeviceUnlockViewModel.consumeCredentialEncrypted() - } - uiState.cipherDecryptDatabase?.let { cipherDecryptDatabase -> - onCredentialDecrypted(cipherDecryptDatabase) - mDeviceUnlockViewModel.consumeCredentialDecrypted() - } - uiState.exception?.let { error -> - Snackbar.make( - coordinatorLayout, - deviceUnlockError(error, this@MainCredentialActivity), - Snackbar.LENGTH_LONG - ).asError().show() - mDeviceUnlockViewModel.exceptionShown() + mDeviceUnlockViewModel?.let { deviceUnlockViewModel -> + deviceUnlockViewModel.uiState.collect { uiState -> + // New value received + uiState.credentialRequiredCipher?.let { cipher -> + deviceUnlockViewModel.encryptCredential( + credential = getCredentialForEncryption(), + cipher = cipher + ) + } + uiState.cipherEncryptDatabase?.let { cipherEncryptDatabase -> + onCredentialEncrypted(cipherEncryptDatabase) + deviceUnlockViewModel.consumeCredentialEncrypted() + } + uiState.cipherDecryptDatabase?.let { cipherDecryptDatabase -> + onCredentialDecrypted(cipherDecryptDatabase) + deviceUnlockViewModel.consumeCredentialDecrypted() + } + uiState.exception?.let { error -> + Snackbar.make( + coordinatorLayout, + deviceUnlockError(error, this@MainCredentialActivity), + Snackbar.LENGTH_LONG + ).asError().show() + deviceUnlockViewModel.exceptionShown() + } } } } @@ -517,7 +524,7 @@ class MainCredentialActivity : DatabaseModeActivity() { } else { // Init Biometric elements if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.connect(databaseFileUri) + mDeviceUnlockViewModel?.connect(databaseFileUri) } } @@ -661,7 +668,7 @@ class MainCredentialActivity : DatabaseModeActivity() { try { menu.findItem(R.id.menu_open_file_read_mode_key) } catch (e: Exception) { - Log.e(TAG, "Unable to find read mode menu") + Log.e(TAG, "Unable to find read mode menu", e) } performedNextEducation(menu) }, @@ -690,7 +697,7 @@ class MainCredentialActivity : DatabaseModeActivity() { }) } } - } catch (ignored: Exception) {} + } catch (_: Exception) {} } } @@ -727,7 +734,7 @@ class MainCredentialActivity : DatabaseModeActivity() { override fun onDestroy() { super.onDestroy() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mDeviceUnlockViewModel.disconnect() + mDeviceUnlockViewModel?.disconnect() } }