feat: passkey selection after close database setting #2187

This commit is contained in:
J-Jamet
2025-09-30 15:19:05 +02:00
parent e07cbc2e14
commit 075f54b286
14 changed files with 48 additions and 28 deletions

View File

@@ -4,7 +4,7 @@ KeePassDX(4.2.0)
* Dialog to manage missing signature #2152 #2155 #2161 #2160 * Dialog to manage missing signature #2152 #2155 #2161 #2160
* Capture error #2159 * Capture error #2159
* Change Passkey Backup Eligibility & Backup State #2135 #2150 * Change Passkey Backup Eligibility & Backup State #2135 #2150
* Search settings #2112 #2181 * Search settings #2112 #2181 #2187
* Autofill refactoring #765 * Autofill refactoring #765
* Small fixes #2157 #2164 #2171 * Small fixes #2157 #2164 #2171

View File

@@ -34,7 +34,7 @@ abstract class DatabaseModeActivity : DatabaseActivity() {
private var mToolbarSpecial: ToolbarSpecial? = null private var mToolbarSpecial: ToolbarSpecial? = null
protected open var mCredentialActivityResultLauncher: ActivityResultLauncher<Intent>? = protected open var mCredentialActivityResultLauncher: ActivityResultLauncher<Intent>? =
this.buildActivityResultLauncher(mTypeMode) this.buildActivityResultLauncher()
open fun onDatabaseBackPressed() { open fun onDatabaseBackPressed() {
if (mSpecialMode != SpecialMode.DEFAULT) if (mSpecialMode != SpecialMode.DEFAULT)

View File

@@ -38,7 +38,6 @@ import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.model.EntryInfo import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.LOCK_ACTION import com.kunzisoft.keepass.utils.LOCK_ACTION
import com.kunzisoft.keepass.utils.getEnumExtra import com.kunzisoft.keepass.utils.getEnumExtra
import com.kunzisoft.keepass.utils.getParcelableExtraCompat import com.kunzisoft.keepass.utils.getParcelableExtraCompat
@@ -60,10 +59,9 @@ object EntrySelectionHelper {
* Finish the activity by passing the result code and by locking the database if necessary * Finish the activity by passing the result code and by locking the database if necessary
*/ */
fun Activity.setActivityResult( fun Activity.setActivityResult(
typeMode: TypeMode,
lockDatabase: Boolean = false, lockDatabase: Boolean = false,
resultCode: Int, resultCode: Int,
data: Intent? = null, data: Intent? = null
) { ) {
when (resultCode) { when (resultCode) {
Activity.RESULT_OK -> Activity.RESULT_OK ->
@@ -74,14 +72,8 @@ object EntrySelectionHelper {
this.finish() this.finish()
if (lockDatabase) { if (lockDatabase) {
when (typeMode) { // Close the database
TypeMode.DEFAULT -> // Close the database
this.sendBroadcast(Intent(LOCK_ACTION)) this.sendBroadcast(Intent(LOCK_ACTION))
TypeMode.MAGIKEYBOARD -> { }
TypeMode.PASSKEY -> { }
TypeMode.AUTOFILL -> if (PreferencesUtil.isAutofillCloseDatabaseEnable(this))
this.sendBroadcast(Intent(LOCK_ACTION))
}
} }
} }
@@ -90,7 +82,6 @@ object EntrySelectionHelper {
* Used recursively, close each activity with return data * Used recursively, close each activity with return data
*/ */
fun AppCompatActivity.buildActivityResultLauncher( fun AppCompatActivity.buildActivityResultLauncher(
typeMode: TypeMode,
lockDatabase: Boolean = false, lockDatabase: Boolean = false,
dataTransformation: (data: Intent?) -> Intent? = { it }, dataTransformation: (data: Intent?) -> Intent? = { it },
): ActivityResultLauncher<Intent> { ): ActivityResultLauncher<Intent> {
@@ -98,7 +89,6 @@ object EntrySelectionHelper {
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { ) {
setActivityResult( setActivityResult(
typeMode,
lockDatabase, lockDatabase,
it.resultCode, it.resultCode,
dataTransformation(it.data) dataTransformation(it.data)

View File

@@ -40,7 +40,6 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSearchIn
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSpecialMode import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSpecialMode
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
import com.kunzisoft.keepass.credentialprovider.SpecialMode import com.kunzisoft.keepass.credentialprovider.SpecialMode
import com.kunzisoft.keepass.credentialprovider.TypeMode
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillComponent import com.kunzisoft.keepass.credentialprovider.autofill.AutofillComponent
import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.addAutofillComponent import com.kunzisoft.keepass.credentialprovider.autofill.AutofillHelper.addAutofillComponent
import com.kunzisoft.keepass.credentialprovider.viewmodel.AutofillLauncherViewModel import com.kunzisoft.keepass.credentialprovider.viewmodel.AutofillLauncherViewModel
@@ -78,6 +77,7 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
autofillLauncherViewModel.initialize()
lifecycleScope.launch { lifecycleScope.launch {
// Initialize the parameters // Initialize the parameters
autofillLauncherViewModel.uiState.collect { uiState -> autofillLauncherViewModel.uiState.collect { uiState ->
@@ -138,7 +138,6 @@ class AutofillLauncherActivity : DatabaseModeActivity() {
} }
is CredentialLauncherViewModel.UIState.SetActivityResult -> { is CredentialLauncherViewModel.UIState.SetActivityResult -> {
setActivityResult( setActivityResult(
typeMode = TypeMode.AUTOFILL,
lockDatabase = uiState.lockDatabase, lockDatabase = uiState.lockDatabase,
resultCode = uiState.resultCode, resultCode = uiState.resultCode,
data = uiState.data data = uiState.data

View File

@@ -119,7 +119,6 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
is CredentialLauncherViewModel.UIState.Loading -> {} is CredentialLauncherViewModel.UIState.Loading -> {}
is CredentialLauncherViewModel.UIState.SetActivityResult -> { is CredentialLauncherViewModel.UIState.SetActivityResult -> {
setActivityResult( setActivityResult(
typeMode = TypeMode.PASSKEY,
lockDatabase = uiState.lockDatabase, lockDatabase = uiState.lockDatabase,
resultCode = uiState.resultCode, resultCode = uiState.resultCode,
data = uiState.data data = uiState.data

View File

@@ -25,6 +25,7 @@ import com.kunzisoft.keepass.database.element.node.NodeIdUUID
import com.kunzisoft.keepass.database.helper.SearchHelper import com.kunzisoft.keepass.database.helper.SearchHelper
import com.kunzisoft.keepass.model.RegisterInfo import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.settings.PreferencesUtil
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,9 +40,15 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
private var mAutofillComponent: AutofillComponent? = null private var mAutofillComponent: AutofillComponent? = null
private var mSelectionResult: ActivityResult? = null private var mSelectionResult: ActivityResult? = null
private var mLockDatabaseAfterSelection: Boolean = false
private val mUiState = MutableStateFlow<UIState>(UIState.Loading) private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
val uiState: StateFlow<UIState> = mUiState val uiState: StateFlow<UIState> = mUiState
fun initialize() {
mLockDatabaseAfterSelection = PreferencesUtil.isAutofillCloseDatabaseEnable(getApplication())
}
override fun onResult() { override fun onResult() {
super.onResult() super.onResult()
mAutofillComponent = null mAutofillComponent = null
@@ -117,7 +124,7 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
database = openedDatabase, database = openedDatabase,
entriesInfo = items entriesInfo = items
) { intent -> ) { intent ->
setResult(intent) setResult(intent, lockDatabase = mLockDatabaseAfterSelection)
} }
}, },
onItemNotFound = { openedDatabase -> onItemNotFound = { openedDatabase ->
@@ -193,7 +200,7 @@ class AutofillLauncherViewModel(application: Application): CredentialLauncherVie
database = database, database = database,
entriesInfo = entries entriesInfo = entries
) { intent -> ) { intent ->
setResult(intent) setResult(intent, lockDatabase = mLockDatabaseAfterSelection)
} }
} }
} }

View File

@@ -21,7 +21,6 @@ import kotlinx.coroutines.launch
abstract class CredentialLauncherViewModel(application: Application): AndroidViewModel(application) { abstract class CredentialLauncherViewModel(application: Application): AndroidViewModel(application) {
protected var mDatabase: ContextualDatabase? = null protected var mDatabase: ContextualDatabase? = null
protected var mLockDatabase: Boolean = true
protected var isResultLauncherRegistered: Boolean = false protected var isResultLauncherRegistered: Boolean = false
@@ -37,20 +36,20 @@ abstract class CredentialLauncherViewModel(application: Application): AndroidVie
isResultLauncherRegistered = false isResultLauncherRegistered = false
} }
fun setResult(intent: Intent) { fun setResult(intent: Intent, lockDatabase: Boolean = false) {
// Remove the launcher register // Remove the launcher register
onResult() onResult()
mCredentialUiState.value = UIState.SetActivityResult( mCredentialUiState.value = UIState.SetActivityResult(
lockDatabase = mLockDatabase, lockDatabase = lockDatabase,
resultCode = RESULT_OK, resultCode = RESULT_OK,
data = intent data = intent
) )
} }
fun cancelResult() { fun cancelResult(lockDatabase: Boolean = false) {
onResult() onResult()
mCredentialUiState.value = UIState.SetActivityResult( mCredentialUiState.value = UIState.SetActivityResult(
lockDatabase = mLockDatabase, lockDatabase = lockDatabase,
resultCode = RESULT_CANCELED resultCode = RESULT_CANCELED
) )
} }

View File

@@ -61,6 +61,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
private var mCreationParameters: PublicKeyCredentialCreationParameters? = null private var mCreationParameters: PublicKeyCredentialCreationParameters? = null
private var mPasskey: Passkey? = null private var mPasskey: Passkey? = null
private var mLockDatabaseAfterSelection: Boolean = false
private var mBackupEligibility: Boolean = true private var mBackupEligibility: Boolean = true
private var mBackupState: Boolean = false private var mBackupState: Boolean = false
@@ -68,6 +69,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
val uiState: StateFlow<UIState> = mUiState val uiState: StateFlow<UIState> = mUiState
fun initialize() { fun initialize() {
mLockDatabaseAfterSelection = PreferencesUtil.isPasskeyCloseDatabaseEnable(getApplication())
mBackupEligibility = PreferencesUtil.isPasskeyBackupEligibilityEnable(getApplication()) mBackupEligibility = PreferencesUtil.isPasskeyBackupEligibilityEnable(getApplication())
mBackupState = PreferencesUtil.isPasskeyBackupStateEnable(getApplication()) mBackupState = PreferencesUtil.isPasskeyBackupStateEnable(getApplication())
} }
@@ -294,7 +296,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
) )
) )
) )
setResult(result) setResult(result, lockDatabase = mLockDatabaseAfterSelection)
} catch (e: SignatureNotFoundException) { } catch (e: SignatureNotFoundException) {
// Request the dialog if signature exception // Request the dialog if signature exception
showAppSignatureDialog(e.temptingApp, nodeId) showAppSignatureDialog(e.temptingApp, nodeId)
@@ -350,7 +352,7 @@ class PasskeyLauncherViewModel(application: Application): CredentialLauncherView
throw IOException("Usage parameters is null") throw IOException("Usage parameters is null")
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
setResult(responseIntent) setResult(responseIntent, lockDatabase = mLockDatabaseAfterSelection)
} }
} }
} }

View File

@@ -690,6 +690,12 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.keyboard_previous_lock_default)) context.resources.getBoolean(R.bool.keyboard_previous_lock_default))
} }
fun isPasskeyCloseDatabaseEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.passkeys_close_database_key),
context.resources.getBoolean(R.bool.passkeys_close_database_default))
}
fun isPasskeyBackupEligibilityEnable(context: Context): Boolean { fun isPasskeyBackupEligibilityEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.passkeys_backup_eligibility_key), return prefs.getBoolean(context.getString(R.string.passkeys_backup_eligibility_key),
@@ -858,6 +864,10 @@ object PreferencesUtil {
context.getString(R.string.keyboard_previous_search_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.keyboard_previous_search_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_previous_fill_in_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.keyboard_previous_fill_in_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.keyboard_previous_lock_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.keyboard_previous_lock_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.passkeys_close_database_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.passkeys_auto_select_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.passkeys_backup_eligibility_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.passkeys_backup_state_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_close_database_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.autofill_close_database_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_inline_suggestions_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.autofill_inline_suggestions_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.autofill_manual_selection_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.autofill_manual_selection_key) -> editor.putBoolean(name, value.toBoolean())

View File

@@ -134,9 +134,11 @@
<string name="settings_autofill_key" translatable="false">settings_autofill_key</string> <string name="settings_autofill_key" translatable="false">settings_autofill_key</string>
<string name="passkeys_explanation_key" translatable="false">passkeys_explanation_key</string> <string name="passkeys_explanation_key" translatable="false">passkeys_explanation_key</string>
<string name="settings_passkeys_key" translatable="false">settings_passkeys_key</string> <string name="settings_passkeys_key" translatable="false">settings_passkeys_key</string>
<string name="passkeys_close_database_key" translatable="false">passkeys_close_database_key</string>
<bool name="passkeys_close_database_default" translatable="false">true</bool>
<string name="passkeys_privileged_apps_key" translatable="false">passkeys_privileged_apps_key</string> <string name="passkeys_privileged_apps_key" translatable="false">passkeys_privileged_apps_key</string>
<string name="passkeys_auto_select_key" translatable="false">passkeys_auto_select_key</string> <string name="passkeys_auto_select_key" translatable="false">passkeys_auto_select_key</string>
<bool name="passkeys_auto_select_default" translatable="false">false</bool> <bool name="passkeys_auto_select_default" translatable="false">true</bool>
<string name="passkeys_backup_eligibility_key" translatable="false">passkeys_backup_eligibility_key</string> <string name="passkeys_backup_eligibility_key" translatable="false">passkeys_backup_eligibility_key</string>
<bool name="passkeys_backup_eligibility_default" translatable="false">true</bool> <bool name="passkeys_backup_eligibility_default" translatable="false">true</bool>
<string name="passkeys_backup_state_key" translatable="false">passkeys_backup_state_key</string> <string name="passkeys_backup_state_key" translatable="false">passkeys_backup_state_key</string>

View File

@@ -422,6 +422,8 @@
<string name="passkeys">Passkeys</string> <string name="passkeys">Passkeys</string>
<string name="passkeys_explanation_summary">Configure Passkeys for fast and secure passwordless login</string> <string name="passkeys_explanation_summary">Configure Passkeys for fast and secure passwordless login</string>
<string name="passkeys_preference_title">Passkeys settings</string> <string name="passkeys_preference_title">Passkeys settings</string>
<string name="passkeys_close_database_title">Close database</string>
<string name="passkeys_close_database_summary">Close the database after a passkey selection</string>
<string name="passkeys_privileged_apps_title">Privileged apps</string> <string name="passkeys_privileged_apps_title">Privileged apps</string>
<string name="passkeys_privileged_apps_summary">Manage browsers in the custom list of privileged apps</string> <string name="passkeys_privileged_apps_summary">Manage browsers in the custom list of privileged apps</string>
<string name="passkeys_privileged_apps_explanation">WARNING: A privileged app acts as a gateway to retrieve the origin of an authentication. Ensure its legitimacy to avoid security issues.</string> <string name="passkeys_privileged_apps_explanation">WARNING: A privileged app acts as a gateway to retrieve the origin of an authentication. Ensure its legitimacy to avoid security issues.</string>

View File

@@ -20,6 +20,11 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory <PreferenceCategory
android:title="@string/general"> android:title="@string/general">
<SwitchPreferenceCompat
android:key="@string/passkeys_close_database_key"
android:title="@string/passkeys_close_database_title"
android:summary="@string/passkeys_close_database_summary"
android:defaultValue="@bool/autofill_close_database_default"/>
<com.kunzisoft.keepass.settings.preference.DialogListExplanationPreference <com.kunzisoft.keepass.settings.preference.DialogListExplanationPreference
android:key="@string/passkeys_privileged_apps_key" android:key="@string/passkeys_privileged_apps_key"
android:title="@string/passkeys_privileged_apps_title" android:title="@string/passkeys_privileged_apps_title"
@@ -29,6 +34,9 @@
android:title="@string/passkeys_auto_select_title" android:title="@string/passkeys_auto_select_title"
android:summary="@string/passkeys_auto_select_summary" android:summary="@string/passkeys_auto_select_summary"
android:defaultValue="@bool/passkeys_auto_select_default"/> android:defaultValue="@bool/passkeys_auto_select_default"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/database">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:key="@string/passkeys_backup_eligibility_key" android:key="@string/passkeys_backup_eligibility_key"
android:title="@string/passkeys_backup_eligibility_title" android:title="@string/passkeys_backup_eligibility_title"

View File

@@ -1,4 +1,5 @@
* Passkeys management #1421 (Thx @cali-95) * Passkeys management #1421 (Thx @cali-95)
* Search settings #2112 #2181 * Search settings #2112 #2181
* Setting to close database after a Passkey selection #2187
* Autofill refactoring #765 * Autofill refactoring #765
* Small fixes #2171 #2150 #2159 * Small fixes #2171 #2150 #2159

View File

@@ -1,4 +1,5 @@
* Gestion de Passkeys #1421 (Thx @cali-95) * Gestion de Passkeys #1421 (Thx @cali-95)
* Paramètres de recherche #2112 #2181 * Paramètres de recherche #2112 #2181
* Paramètre de fermeture de la base après une sélection de Passkey #2187
* Refonte du remplissage automatique #765 * Refonte du remplissage automatique #765
* Petites corrections #2171 #2150 #2159 * Petites corrections #2171 #2150 #2159