From 98ac199dc6af81152d25061614fbf71357712206 Mon Sep 17 00:00:00 2001 From: Brian Yencho Date: Mon, 29 Jan 2024 22:27:25 -0600 Subject: [PATCH] BIT-1642: Pre-populate the add item screen URI during autofill (#863) --- .../feature/addedit/VaultAddEditViewModel.kt | 30 ++++++++++--- .../util/AutofillSelectionDataExtensions.kt | 31 ++++++++++++++ .../addedit/VaultAddEditViewModelTest.kt | 40 +++++++++++++++++- .../AutofillSelectionDataExtensionsTest.kt | 42 +++++++++++++++++++ 4 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt create mode 100644 app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt index 5848ce0813..a5a33f1a52 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModel.kt @@ -7,7 +7,10 @@ import com.bitwarden.core.CipherView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult +import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData +import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager +import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository @@ -27,6 +30,7 @@ import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField +import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toDefaultAddTypeContent import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState import com.x8bit.bitwarden.ui.vault.feature.vault.util.toCipherView import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType @@ -59,27 +63,41 @@ private const val KEY_STATE = "state" * @param savedStateHandle Handles the navigation arguments of this ViewModel. */ @HiltViewModel -@Suppress("TooManyFunctions", "LargeClass") +@Suppress("TooManyFunctions", "LargeClass", "LongParameterList") class VaultAddEditViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val authRepository: AuthRepository, private val clipboardManager: BitwardenClipboardManager, private val vaultRepository: VaultRepository, private val generatorRepository: GeneratorRepository, + private val specialCircumstanceManager: SpecialCircumstanceManager, private val resourceManager: ResourceManager, ) : BaseViewModel( // We load the state from the savedStateHandle for testing purposes. initialState = savedStateHandle[KEY_STATE] ?: run { val vaultAddEditType = VaultAddEditArgs(savedStateHandle).vaultAddEditType + + // Check for autofill data to pre-populate + val autofillSelectionData: AutofillSelectionData? = + when (val specialCircumstance = specialCircumstanceManager.specialCircumstance) { + is SpecialCircumstance.AutofillSelection -> { + specialCircumstance.autofillSelectionData + } + + else -> null + } + val defaultAddTypeContent = autofillSelectionData + ?.toDefaultAddTypeContent() + ?: VaultAddEditState.ViewState.Content( + common = VaultAddEditState.ViewState.Content.Common(), + type = VaultAddEditState.ViewState.Content.ItemType.Login(), + ) + VaultAddEditState( vaultAddEditType = vaultAddEditType, viewState = when (vaultAddEditType) { - VaultAddEditType.AddItem -> VaultAddEditState.ViewState.Content( - common = VaultAddEditState.ViewState.Content.Common(), - type = VaultAddEditState.ViewState.Content.ItemType.Login(), - ) - + VaultAddEditType.AddItem -> defaultAddTypeContent is VaultAddEditType.EditItem -> VaultAddEditState.ViewState.Loading is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading }, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt new file mode 100644 index 0000000000..d687892ac6 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt @@ -0,0 +1,31 @@ +package com.x8bit.bitwarden.ui.vault.feature.addedit.util + +import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData +import com.x8bit.bitwarden.ui.platform.base.util.toHostOrPathOrNull +import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState + +/** + * Returns pre-filled content that may be used for an "add" type + * [VaultAddEditState.ViewState.Content]. + */ +fun AutofillSelectionData.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content { + val uri = this.uri + val simpleUri = uri?.toHostOrPathOrNull() + val defaultAddType = when (this.type) { + AutofillSelectionData.Type.CARD -> { + VaultAddEditState.ViewState.Content.ItemType.Card() + } + + AutofillSelectionData.Type.LOGIN -> { + VaultAddEditState.ViewState.Content.ItemType.Login( + uri = uri.orEmpty(), + ) + } + } + return VaultAddEditState.ViewState.Content( + common = VaultAddEditState.ViewState.Content.Common( + name = simpleUri.orEmpty(), + ), + type = defaultAddType, + ) +} diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt index 37da41547a..f6f0535ea6 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt @@ -7,7 +7,11 @@ import com.bitwarden.core.UriMatchType import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult +import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData +import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager +import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager +import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository @@ -27,6 +31,7 @@ import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField +import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toDefaultAddTypeContent import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand @@ -74,6 +79,8 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { every { getVaultItemStateFlow(DEFAULT_EDIT_ITEM_ID) } returns mutableVaultItemFlow every { totpCodeFlow } returns totpTestCodeFlow } + private val specialCircumstanceManager: SpecialCircumstanceManager = + SpecialCircumstanceManagerImpl() private val generatorRepository: GeneratorRepository = FakeGeneratorRepository() @@ -107,7 +114,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { } @Test - fun `initial add state should be correct`() = runTest { + fun `initial add state should be correct when not autofill`() = runTest { val vaultAddEditType = VaultAddEditType.AddItem val initState = createVaultAddItemState(vaultAddEditType = vaultAddEditType) val viewModel = createAddVaultItemViewModel( @@ -122,6 +129,35 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { } } + @Test + fun `initial add state should be correct when autofill`() = runTest { + val autofillSelectionData = AutofillSelectionData( + type = AutofillSelectionData.Type.LOGIN, + uri = "https://www.test.com", + ) + specialCircumstanceManager.specialCircumstance = SpecialCircumstance.AutofillSelection( + autofillSelectionData = autofillSelectionData, + shouldFinishWhenComplete = true, + ) + val autofillContentState = autofillSelectionData.toDefaultAddTypeContent() + val vaultAddEditType = VaultAddEditType.AddItem + val initState = createVaultAddItemState( + vaultAddEditType = vaultAddEditType, + commonContentViewState = autofillContentState.common, + typeContentViewState = autofillContentState.type, + ) + val viewModel = createAddVaultItemViewModel( + savedStateHandle = createSavedStateHandleWithState( + state = initState, + vaultAddEditType = vaultAddEditType, + ), + ) + assertEquals(initState, viewModel.stateFlow.value) + verify(exactly = 0) { + vaultRepository.getVaultItemStateFlow(DEFAULT_EDIT_ITEM_ID) + } + } + @Test fun `initial edit state should be correct`() = runTest { val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID) @@ -1430,6 +1466,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { clipboardManager = clipboardManager, vaultRepository = vaultRepository, generatorRepository = generatorRepository, + specialCircumstanceManager = specialCircumstanceManager, resourceManager = resourceManager, authRepository = authRepository, ) @@ -1950,6 +1987,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { clipboardManager = bitwardenClipboardManager, vaultRepository = vaultRepo, generatorRepository = generatorRepo, + specialCircumstanceManager = specialCircumstanceManager, resourceManager = bitwardenResourceManager, authRepository = authRepository, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt new file mode 100644 index 0000000000..cfc7299d1a --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt @@ -0,0 +1,42 @@ +package com.x8bit.bitwarden.ui.vault.feature.addedit.util + +import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData +import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class AutofillSelectionDataExtensionsTest { + @Test + fun `toDefaultAddTypeContent for a Card type should return the correct Content`() { + assertEquals( + VaultAddEditState.ViewState.Content( + common = VaultAddEditState.ViewState.Content.Common(), + type = VaultAddEditState.ViewState.Content.ItemType.Card(), + ), + AutofillSelectionData( + type = AutofillSelectionData.Type.CARD, + uri = null, + ) + .toDefaultAddTypeContent(), + ) + } + + @Test + fun `toDefaultAddTypeContent for a Login type should return the correct Content`() { + assertEquals( + VaultAddEditState.ViewState.Content( + common = VaultAddEditState.ViewState.Content.Common( + name = "www.test.com", + ), + type = VaultAddEditState.ViewState.Content.ItemType.Login( + uri = "https://www.test.com", + ), + ), + AutofillSelectionData( + type = AutofillSelectionData.Type.LOGIN, + uri = "https://www.test.com", + ) + .toDefaultAddTypeContent(), + ) + } +}