mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 07:05:35 +03:00
PM-9406: Add passkey management to autofill settings (#3392)
This commit is contained in:
parent
646566edd8
commit
82096e0625
7 changed files with 134 additions and 1 deletions
|
@ -23,6 +23,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||||
* @param text The label for the row as a [String].
|
* @param text The label for the row as a [String].
|
||||||
* @param onConfirmClick The callback when the confirm button of the dialog is clicked.
|
* @param onConfirmClick The callback when the confirm button of the dialog is clicked.
|
||||||
* @param modifier The modifier to be applied to the layout.
|
* @param modifier The modifier to be applied to the layout.
|
||||||
|
* @param description An optional description label to be displayed below the [text].
|
||||||
* @param withDivider Indicates if a divider should be drawn on the bottom of the row, defaults
|
* @param withDivider Indicates if a divider should be drawn on the bottom of the row, defaults
|
||||||
* to `true`.
|
* to `true`.
|
||||||
* @param dialogTitle The title of the dialog displayed when the user clicks this item.
|
* @param dialogTitle The title of the dialog displayed when the user clicks this item.
|
||||||
|
@ -37,6 +38,7 @@ fun BitwardenExternalLinkRow(
|
||||||
text: String,
|
text: String,
|
||||||
onConfirmClick: () -> Unit,
|
onConfirmClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
description: String? = null,
|
||||||
withDivider: Boolean = true,
|
withDivider: Boolean = true,
|
||||||
dialogTitle: String,
|
dialogTitle: String,
|
||||||
dialogMessage: String,
|
dialogMessage: String,
|
||||||
|
@ -46,6 +48,7 @@ fun BitwardenExternalLinkRow(
|
||||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
BitwardenTextRow(
|
BitwardenTextRow(
|
||||||
text = text,
|
text = text,
|
||||||
|
description = description,
|
||||||
onClick = { shouldShowDialog = true },
|
onClick = { shouldShowDialog = true },
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
withDivider = withDivider,
|
withDivider = withDivider,
|
||||||
|
|
|
@ -38,6 +38,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenExternalLinkRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenWideSwitch
|
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenWideSwitch
|
||||||
|
@ -79,6 +80,10 @@ fun AutoFillScreen(
|
||||||
AutoFillEvent.NavigateToBlockAutoFill -> {
|
AutoFillEvent.NavigateToBlockAutoFill -> {
|
||||||
onNavigateToBlockAutoFillScreen()
|
onNavigateToBlockAutoFillScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AutoFillEvent.NavigateToSettings -> {
|
||||||
|
intentManager.startCredentialManagerSettings(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +155,22 @@ fun AutoFillScreen(
|
||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (state.showPasskeyManagementRow) {
|
||||||
|
BitwardenExternalLinkRow(
|
||||||
|
text = stringResource(id = R.string.passkey_management),
|
||||||
|
description = stringResource(
|
||||||
|
id = R.string.passkey_management_explanation_long,
|
||||||
|
),
|
||||||
|
onConfirmClick = remember(viewModel) {
|
||||||
|
{ viewModel.trySendAction(AutoFillAction.PasskeyManagementClick) }
|
||||||
|
},
|
||||||
|
dialogTitle = stringResource(id = R.string.continue_to_device_settings),
|
||||||
|
dialogMessage = stringResource(
|
||||||
|
id = R.string.set_bitwarden_as_passkey_manager_description,
|
||||||
|
),
|
||||||
|
withDivider = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
BitwardenListHeaderText(
|
BitwardenListHeaderText(
|
||||||
label = stringResource(id = R.string.additional_options),
|
label = stringResource(id = R.string.additional_options),
|
||||||
|
|
|
@ -35,6 +35,7 @@ class AutoFillViewModel @Inject constructor(
|
||||||
isCopyTotpAutomaticallyEnabled = !settingsRepository.isAutoCopyTotpDisabled,
|
isCopyTotpAutomaticallyEnabled = !settingsRepository.isAutoCopyTotpDisabled,
|
||||||
isUseInlineAutoFillEnabled = settingsRepository.isInlineAutofillEnabled,
|
isUseInlineAutoFillEnabled = settingsRepository.isInlineAutofillEnabled,
|
||||||
showInlineAutofillOption = !isBuildVersionBelow(Build.VERSION_CODES.R),
|
showInlineAutofillOption = !isBuildVersionBelow(Build.VERSION_CODES.R),
|
||||||
|
showPasskeyManagementRow = !isBuildVersionBelow(Build.VERSION_CODES.UPSIDE_DOWN_CAKE),
|
||||||
defaultUriMatchType = settingsRepository.defaultUriMatchType,
|
defaultUriMatchType = settingsRepository.defaultUriMatchType,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
|
@ -61,6 +62,7 @@ class AutoFillViewModel @Inject constructor(
|
||||||
is AutoFillAction.DefaultUriMatchTypeSelect -> handleDefaultUriMatchTypeSelect(action)
|
is AutoFillAction.DefaultUriMatchTypeSelect -> handleDefaultUriMatchTypeSelect(action)
|
||||||
AutoFillAction.BlockAutoFillClick -> handleBlockAutoFillClick()
|
AutoFillAction.BlockAutoFillClick -> handleBlockAutoFillClick()
|
||||||
is AutoFillAction.UseInlineAutofillClick -> handleUseInlineAutofillClick(action)
|
is AutoFillAction.UseInlineAutofillClick -> handleUseInlineAutofillClick(action)
|
||||||
|
AutoFillAction.PasskeyManagementClick -> handlePasskeyManagementClick()
|
||||||
is AutoFillAction.Internal.AutofillEnabledUpdateReceive -> {
|
is AutoFillAction.Internal.AutofillEnabledUpdateReceive -> {
|
||||||
handleAutofillEnabledUpdateReceive(action)
|
handleAutofillEnabledUpdateReceive(action)
|
||||||
}
|
}
|
||||||
|
@ -95,6 +97,10 @@ class AutoFillViewModel @Inject constructor(
|
||||||
mutableStateFlow.update { it.copy(isUseInlineAutoFillEnabled = action.isEnabled) }
|
mutableStateFlow.update { it.copy(isUseInlineAutoFillEnabled = action.isEnabled) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handlePasskeyManagementClick() {
|
||||||
|
sendEvent(AutoFillEvent.NavigateToSettings)
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleDefaultUriMatchTypeSelect(action: AutoFillAction.DefaultUriMatchTypeSelect) {
|
private fun handleDefaultUriMatchTypeSelect(action: AutoFillAction.DefaultUriMatchTypeSelect) {
|
||||||
settingsRepository.defaultUriMatchType = action.defaultUriMatchType
|
settingsRepository.defaultUriMatchType = action.defaultUriMatchType
|
||||||
mutableStateFlow.update {
|
mutableStateFlow.update {
|
||||||
|
@ -125,6 +131,7 @@ data class AutoFillState(
|
||||||
val isCopyTotpAutomaticallyEnabled: Boolean,
|
val isCopyTotpAutomaticallyEnabled: Boolean,
|
||||||
val isUseInlineAutoFillEnabled: Boolean,
|
val isUseInlineAutoFillEnabled: Boolean,
|
||||||
val showInlineAutofillOption: Boolean,
|
val showInlineAutofillOption: Boolean,
|
||||||
|
val showPasskeyManagementRow: Boolean,
|
||||||
val defaultUriMatchType: UriMatchType,
|
val defaultUriMatchType: UriMatchType,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
|
@ -155,6 +162,11 @@ sealed class AutoFillEvent {
|
||||||
*/
|
*/
|
||||||
data object NavigateToBlockAutoFill : AutoFillEvent()
|
data object NavigateToBlockAutoFill : AutoFillEvent()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to device settings.
|
||||||
|
*/
|
||||||
|
data object NavigateToSettings : AutoFillEvent()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a toast with the given [Text].
|
* Displays a toast with the given [Text].
|
||||||
*/
|
*/
|
||||||
|
@ -212,6 +224,11 @@ sealed class AutoFillAction {
|
||||||
val isEnabled: Boolean,
|
val isEnabled: Boolean,
|
||||||
) : AutoFillAction()
|
) : AutoFillAction()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked passkey management button.
|
||||||
|
*/
|
||||||
|
data object PasskeyManagementClick : AutoFillAction()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal actions.
|
* Internal actions.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.x8bit.bitwarden.ui.platform.manager.intent
|
package com.x8bit.bitwarden.ui.platform.manager.intent
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
@ -37,6 +38,11 @@ interface IntentManager {
|
||||||
*/
|
*/
|
||||||
fun startApplicationDetailsSettingsActivity()
|
fun startApplicationDetailsSettingsActivity()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the credential manager settings.
|
||||||
|
*/
|
||||||
|
fun startCredentialManagerSettings(context: Context)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start an activity to view the given [uri] in an external browser.
|
* Start an activity to view the given [uri] in an external browser.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
|
@ -19,6 +20,7 @@ import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.credentials.CredentialManager
|
||||||
import com.x8bit.bitwarden.BuildConfig
|
import com.x8bit.bitwarden.BuildConfig
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag
|
import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag
|
||||||
|
@ -117,6 +119,12 @@ class IntentManagerImpl(
|
||||||
startActivity(intent = intent)
|
startActivity(intent = intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun startCredentialManagerSettings(context: Context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
CredentialManager.create(context).createSettingsPendingIntent().send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun launchUri(uri: Uri) {
|
override fun launchUri(uri: Uri) {
|
||||||
val newUri = if (uri.scheme == null) {
|
val newUri = if (uri.scheme == null) {
|
||||||
uri.buildUpon().scheme("https").build()
|
uri.buildUpon().scheme("https").build()
|
||||||
|
|
|
@ -20,7 +20,9 @@ import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||||
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import io.mockk.runs
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
@ -42,6 +44,7 @@ class AutoFillScreenTest : BaseComposeTest() {
|
||||||
}
|
}
|
||||||
private val intentManager: IntentManager = mockk {
|
private val intentManager: IntentManager = mockk {
|
||||||
every { startSystemAutofillSettingsActivity() } answers { isSystemSettingsRequestSuccess }
|
every { startSystemAutofillSettingsActivity() } answers { isSystemSettingsRequestSuccess }
|
||||||
|
every { startCredentialManagerSettings(any()) } just runs
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -94,6 +97,15 @@ class AutoFillScreenTest : BaseComposeTest() {
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on NavigateToSettings should attempt to navigate to credential manager settings`() {
|
||||||
|
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToSettings)
|
||||||
|
|
||||||
|
verify { intentManager.startCredentialManagerSettings(any()) }
|
||||||
|
|
||||||
|
composeTestRule.assertNoDialogExists()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `on autofill settings fallback dialog Ok click should dismiss the dialog`() {
|
fun `on autofill settings fallback dialog Ok click should dismiss the dialog`() {
|
||||||
isSystemSettingsRequestSuccess = false
|
isSystemSettingsRequestSuccess = false
|
||||||
|
@ -187,6 +199,33 @@ class AutoFillScreenTest : BaseComposeTest() {
|
||||||
.assertIsNotEnabled()
|
.assertIsNotEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `on passkey management click should display confirmation dialog and confirm click should emit PasskeyManagementClick`() {
|
||||||
|
composeTestRule.onNode(isDialog()).assertDoesNotExist()
|
||||||
|
composeTestRule
|
||||||
|
.onNodeWithText("Passkey management")
|
||||||
|
.performClick()
|
||||||
|
composeTestRule.onNode(isDialog()).assertExists()
|
||||||
|
composeTestRule
|
||||||
|
.onAllNodesWithText("Continue")
|
||||||
|
.filterToOne(hasAnyAncestor(isDialog()))
|
||||||
|
.performClick()
|
||||||
|
composeTestRule.onNode(isDialog()).assertDoesNotExist()
|
||||||
|
verify { viewModel.trySendAction(AutoFillAction.PasskeyManagementClick) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `passkey management row should not appear according to state`() {
|
||||||
|
mutableStateFlow.update {
|
||||||
|
it.copy(
|
||||||
|
showPasskeyManagementRow = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composeTestRule.onNode(isDialog()).assertDoesNotExist()
|
||||||
|
composeTestRule.onNodeWithText("Passkey management").assertDoesNotExist()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `use inline autofill should be displayed according to state`() {
|
fun `use inline autofill should be displayed according to state`() {
|
||||||
mutableStateFlow.update {
|
mutableStateFlow.update {
|
||||||
|
@ -356,5 +395,6 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState(
|
||||||
isCopyTotpAutomaticallyEnabled = false,
|
isCopyTotpAutomaticallyEnabled = false,
|
||||||
isUseInlineAutoFillEnabled = false,
|
isUseInlineAutoFillEnabled = false,
|
||||||
showInlineAutofillOption = true,
|
showInlineAutofillOption = true,
|
||||||
|
showPasskeyManagementRow = true,
|
||||||
defaultUriMatchType = UriMatchType.DOMAIN,
|
defaultUriMatchType = UriMatchType.DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
|
@ -50,12 +50,20 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state should be correct when not set`() {
|
fun `initial state should be correct when not set`() {
|
||||||
|
mockkStatic(::isBuildVersionBelow)
|
||||||
|
every { isBuildVersionBelow(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) } returns false
|
||||||
|
|
||||||
val viewModel = createViewModel(state = null)
|
val viewModel = createViewModel(state = null)
|
||||||
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
||||||
|
|
||||||
|
unmockkStatic(::isBuildVersionBelow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state should be correct when set`() {
|
fun `initial state should be correct when set`() {
|
||||||
|
mockkStatic(::isBuildVersionBelow)
|
||||||
|
every { isBuildVersionBelow(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) } returns false
|
||||||
|
|
||||||
mutableIsAutofillEnabledStateFlow.value = true
|
mutableIsAutofillEnabledStateFlow.value = true
|
||||||
val state = DEFAULT_STATE.copy(
|
val state = DEFAULT_STATE.copy(
|
||||||
isAutoFillServicesEnabled = true,
|
isAutoFillServicesEnabled = true,
|
||||||
|
@ -63,6 +71,23 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
val viewModel = createViewModel(state = state)
|
val viewModel = createViewModel(state = state)
|
||||||
assertEquals(state, viewModel.stateFlow.value)
|
assertEquals(state, viewModel.stateFlow.value)
|
||||||
|
|
||||||
|
unmockkStatic(::isBuildVersionBelow)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `initial state should be correct when sdk is below min`() {
|
||||||
|
mockkStatic(::isBuildVersionBelow)
|
||||||
|
every { isBuildVersionBelow(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) } returns true
|
||||||
|
|
||||||
|
val expected = DEFAULT_STATE.copy(
|
||||||
|
showPasskeyManagementRow = false,
|
||||||
|
)
|
||||||
|
val viewModel = createViewModel(state = null)
|
||||||
|
|
||||||
|
assertEquals(expected, viewModel.stateFlow.value)
|
||||||
|
|
||||||
|
unmockkStatic(::isBuildVersionBelow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -70,7 +95,10 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||||
every { isBuildVersionBelow(Build.VERSION_CODES.R) } returns false
|
every { isBuildVersionBelow(Build.VERSION_CODES.R) } returns false
|
||||||
val viewModel = createViewModel(state = null)
|
val viewModel = createViewModel(state = null)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
DEFAULT_STATE.copy(showInlineAutofillOption = true),
|
DEFAULT_STATE.copy(
|
||||||
|
showInlineAutofillOption = true,
|
||||||
|
showPasskeyManagementRow = false,
|
||||||
|
),
|
||||||
viewModel.stateFlow.value,
|
viewModel.stateFlow.value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -175,6 +203,15 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
||||||
verify { settingsRepository.isInlineAutofillEnabled = false }
|
verify { settingsRepository.isInlineAutofillEnabled = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `on PasskeyManagementClick should emit NavigateToSettings`() = runTest {
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.eventFlow.test {
|
||||||
|
viewModel.trySendAction(AutoFillAction.PasskeyManagementClick)
|
||||||
|
assertEquals(AutoFillEvent.NavigateToSettings, awaitItem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `on DefaultUriMatchTypeSelect should update the state and save the new value to settings`() {
|
fun `on DefaultUriMatchTypeSelect should update the state and save the new value to settings`() {
|
||||||
|
@ -211,5 +248,6 @@ private val DEFAULT_STATE: AutoFillState = AutoFillState(
|
||||||
isCopyTotpAutomaticallyEnabled = false,
|
isCopyTotpAutomaticallyEnabled = false,
|
||||||
isUseInlineAutoFillEnabled = true,
|
isUseInlineAutoFillEnabled = true,
|
||||||
showInlineAutofillOption = false,
|
showInlineAutofillOption = false,
|
||||||
|
showPasskeyManagementRow = true,
|
||||||
defaultUriMatchType = UriMatchType.DOMAIN,
|
defaultUriMatchType = UriMatchType.DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue