diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/model/AutofillSelectionData.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/model/AutofillSelectionData.kt
index 0327ebf2413..5254eb93efd 100644
--- a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/model/AutofillSelectionData.kt
+++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/model/AutofillSelectionData.kt
@@ -23,6 +23,7 @@ data class AutofillSelectionData(
enum class Type {
CARD,
LOGIN,
+ IDENTITY,
}
/**
diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensions.kt
index 4af7c3545ae..67baa1be9de 100644
--- a/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensions.kt
+++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensions.kt
@@ -37,10 +37,7 @@ fun FilledData.buildVaultItemDataset(
type = when (this.originalPartition) {
is AutofillPartition.Card -> AutofillSelectionData.Type.CARD
is AutofillPartition.Login -> AutofillSelectionData.Type.LOGIN
- // AutofillSelectionData.Type has no IDENTITY value yet (deliberately deferred -- it
- // cascades into the vault item listing/add-edit UI flows). This is a no-op placeholder
- // today since nothing yet produces an Identity-only partition.
- is AutofillPartition.Identity -> AutofillSelectionData.Type.LOGIN
+ is AutofillPartition.Identity -> AutofillSelectionData.Type.IDENTITY
},
uri = this.uri,
)
diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensions.kt
index 89e5f2da7e9..ffad59450da 100644
--- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensions.kt
+++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensions.kt
@@ -10,4 +10,5 @@ fun AutofillSelectionData.Type.toVaultItemListingType(): VaultItemListingType =
when (this) {
AutofillSelectionData.Type.CARD -> VaultItemListingType.Card
AutofillSelectionData.Type.LOGIN -> VaultItemListingType.Login
+ AutofillSelectionData.Type.IDENTITY -> VaultItemListingType.Identity
}
diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt
index d7b0927540d..479aa2980f0 100644
--- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt
+++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensions.kt
@@ -32,6 +32,10 @@ fun AutofillSelectionData.toDefaultAddTypeContent(
),
)
}
+
+ AutofillSelectionData.Type.IDENTITY -> {
+ VaultAddEditState.ViewState.Content.ItemType.Identity()
+ }
}
return VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt
index 9107d6871d8..cc747a24ac8 100644
--- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt
+++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt
@@ -2867,6 +2867,20 @@ class VaultItemListingViewModel @Inject constructor(
)
}
}
+
+ AutofillSelectionData.Type.IDENTITY -> {
+ this.map { vaultData ->
+ vaultData.copy(
+ decryptCipherListResult = vaultData.decryptCipherListResult.copy(
+ successes = vaultData
+ .decryptCipherListResult
+ .successes
+ .filter { it.type is CipherListViewType.Identity },
+ failures = emptyList(),
+ ),
+ )
+ }
+ }
}
}
@@ -3039,19 +3053,23 @@ data class VaultItemListingState(
val appBarTitle: Text
get() = autofillSelectionData
?.let { data ->
- data.uri
- ?.toHostOrPathOrNull()
- ?.let {
- when (data.type) {
- AutofillSelectionData.Type.CARD -> {
- BitwardenString.select_a_card_for_x.asText(it)
- }
+ when (data.type) {
+ AutofillSelectionData.Type.IDENTITY -> {
+ BitwardenString.choose_an_identity.asText()
+ }
- AutofillSelectionData.Type.LOGIN -> {
- BitwardenString.items_for_uri.asText(it)
- }
- }
+ AutofillSelectionData.Type.CARD -> {
+ data.uri
+ ?.toHostOrPathOrNull()
+ ?.let { BitwardenString.select_a_card_for_x.asText(it) }
+ }
+
+ AutofillSelectionData.Type.LOGIN -> {
+ data.uri
+ ?.toHostOrPathOrNull()
+ ?.let { BitwardenString.items_for_uri.asText(it) }
}
+ }
}
?: createCredentialRequest
?.relyingPartyIdOrNull
diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensionsTest.kt
index 30881e342ad..81c4a4017af 100644
--- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensionsTest.kt
+++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/util/AutofillSelectionDataExtensionsTest.kt
@@ -11,6 +11,7 @@ class AutofillSelectionDataExtensionsTest {
mapOf(
AutofillSelectionData.Type.CARD to VaultItemListingType.Card,
AutofillSelectionData.Type.LOGIN to VaultItemListingType.Login,
+ AutofillSelectionData.Type.IDENTITY to VaultItemListingType.Identity,
)
.forEach { (type, expected) ->
assertEquals(
diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt
index 4b0f6d9c1a5..5308b5da306 100644
--- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt
+++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/addedit/util/AutofillSelectionDataExtensionsTest.kt
@@ -39,6 +39,23 @@ class AutofillSelectionDataExtensionsTest {
)
}
+ @Test
+ fun `toDefaultAddTypeContent for an Identity type should return the correct Content`() {
+ assertEquals(
+ VaultAddEditState.ViewState.Content(
+ common = VaultAddEditState.ViewState.Content.Common(),
+ isIndividualVaultDisabled = false,
+ type = VaultAddEditState.ViewState.Content.ItemType.Identity(),
+ ),
+ AutofillSelectionData(
+ type = AutofillSelectionData.Type.IDENTITY,
+ framework = AutofillSelectionData.Framework.AUTOFILL,
+ uri = null,
+ )
+ .toDefaultAddTypeContent(isIndividualVaultDisabled = false),
+ )
+ }
+
@Test
fun `toDefaultAddTypeContent for a Login type should return the correct Content`() {
every { UUID.randomUUID().toString() } returns "uuid"
diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt
index d25192a97e5..faf87247bb0 100644
--- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt
+++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingScreenTest.kt
@@ -1371,6 +1371,33 @@ class VaultItemListingScreenTest : BitwardenComposeTest() {
.assertIsDisplayed()
}
+ @Test
+ fun `topBar title should be displayed for identity autofill selection regardless of the uri`() {
+ mutableStateFlow.update {
+ it.copy(
+ itemListingType = VaultItemListingState.ItemListingType.Vault.Identity,
+ autofillSelectionData = AUTOFILL_SELECTION_DATA.copy(
+ type = AutofillSelectionData.Type.IDENTITY,
+ uri = null,
+ ),
+ )
+ }
+ composeTestRule
+ .onNodeWithText(text = "Choose an identity")
+ .assertIsDisplayed()
+
+ mutableStateFlow.update {
+ it.copy(
+ autofillSelectionData = AUTOFILL_SELECTION_DATA.copy(
+ type = AutofillSelectionData.Type.IDENTITY,
+ ),
+ )
+ }
+ composeTestRule
+ .onNodeWithText(text = "Choose an identity")
+ .assertIsDisplayed()
+ }
+
@Test
fun `on overflow item click should display menu`() {
composeTestRule
diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt
index 9aee5faef81..c03b62138d7 100644
--- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt
+++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt
@@ -2906,6 +2906,76 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
)
}
+ @Suppress("MaxLineLength")
+ @Test
+ fun `vaultDataStateFlow Loaded with items and autofill filtering for Identity should update ViewState to Content with filtered data`() =
+ runTest {
+ setupMockUri()
+
+ val cipherView1 = createMockCipherListView(
+ number = 1,
+ type = CipherListViewType.Identity,
+ )
+ val cipherView2 = createMockCipherListView(
+ number = 2,
+ type = CipherListViewType.Login(
+ createMockLoginListView(number = 2),
+ ),
+ )
+
+ val autofillSelectionData = AutofillSelectionData(
+ type = AutofillSelectionData.Type.IDENTITY,
+ framework = AutofillSelectionData.Framework.AUTOFILL,
+ uri = "https://www.test.com",
+ )
+ specialCircumstanceManager.specialCircumstance =
+ SpecialCircumstance.AutofillSelection(
+ autofillSelectionData = autofillSelectionData,
+ shouldFinishWhenComplete = true,
+ )
+ val dataState = DataState.Loaded(
+ data = VaultData(
+ decryptCipherListResult = createMockDecryptCipherListResult(
+ number = 1,
+ successes = listOf(cipherView1, cipherView2),
+ ),
+ folderViewList = listOf(createMockFolderView(number = 1)),
+ collectionViewList = listOf(createMockCollectionView(number = 1)),
+ sendViewList = listOf(createMockSendView(number = 1)),
+ ),
+ )
+
+ val viewModel = createVaultItemListingViewModel(
+ savedStateHandle = createSavedStateHandleWithVaultItemListingType(
+ vaultItemListingType = VaultItemListingType.Identity,
+ ),
+ )
+
+ mutableVaultDataStateFlow.value = dataState
+
+ assertEquals(
+ createVaultItemListingState(
+ itemListingType = VaultItemListingState.ItemListingType.Vault.Identity,
+ viewState = VaultItemListingState.ViewState.Content(
+ displayCollectionList = emptyList(),
+ displayItemList = listOf(
+ createMockDisplayItemForCipher(
+ number = 1,
+ cipherType = CipherType.IDENTITY,
+ subtitle = "mockSubtitle-1",
+ secondSubtitleTestTag = "PasskeySite",
+ subtitleTestTag = "PasswordName",
+ isAutofill = true,
+ ),
+ ),
+ displayFolderList = emptyList(),
+ ),
+ )
+ .copy(autofillSelectionData = autofillSelectionData),
+ viewModel.stateFlow.value,
+ )
+ }
+
@Suppress("MaxLineLength")
@Test
fun `vaultDataStateFlow Loaded with items and totp data filtering should update ViewState to Content with filtered data`() =
diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataUtil.kt
index f08bead83a5..a2d55a0bbc9 100644
--- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataUtil.kt
+++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/itemlisting/util/VaultItemListingDataUtil.kt
@@ -97,7 +97,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
- subtitleTestTag = "CipherSubTitleLabel",
+ subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_note),
extraIconList = persistentListOf(
IconData.Local(
@@ -145,7 +145,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
- subtitleTestTag = "CipherSubTitleLabel",
+ subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_payment_card),
extraIconList = persistentListOf(
IconData.Local(
@@ -197,7 +197,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
- subtitleTestTag = "CipherSubTitleLabel",
+ subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_id_card),
extraIconList = persistentListOf(
IconData.Local(
@@ -225,9 +225,9 @@ fun createMockDisplayItemForCipher(
ListingItemOverflowAction.VaultAction.ArchiveClick(cipherId = "mockId-$number"),
),
optionsTestTag = "CipherOptionsButton",
- isAutofill = false,
- isCredentialCreation = false,
- shouldShowMasterPasswordReprompt = false,
+ isAutofill = isAutofill,
+ isCredentialCreation = isCredentialCreation,
+ shouldShowMasterPasswordReprompt = shouldShowMasterPasswordReprompt,
iconTestTag = "IdentityCipherIcon",
itemType = VaultItemListingState.DisplayItem.ItemType.Vault(type = cipherType),
)
@@ -241,7 +241,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
- subtitleTestTag = "CipherSubTitleLabel",
+ subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_ssh_key),
extraIconList = persistentListOf(
IconData.Local(
@@ -285,7 +285,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
- subtitleTestTag = "CipherSubTitleLabel",
+ subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_payment_card),
extraIconList = persistentListOf(
IconData.Local(
@@ -329,7 +329,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
- subtitleTestTag = "CipherSubTitleLabel",
+ subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_note),
extraIconList = persistentListOf(
IconData.Local(
@@ -373,7 +373,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
- subtitleTestTag = "CipherSubTitleLabel",
+ subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_note),
extraIconList = persistentListOf(
IconData.Local(
diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml
index f38700c934f..94d7682b026 100644
--- a/ui/src/main/res/values/strings.xml
+++ b/ui/src/main/res/values/strings.xml
@@ -1161,6 +1161,7 @@ Do you want to switch to this account?
- Bitwarden could not decrypt %1$d vault items. Copy and share this error report with customer success to avoid additional data loss.
Select a card for %s
+ Choose an identity
Advanced options
More about match detection
Keep your credentials secure