BIT-1661: Pre-populate Add Item screen during autofill save (#913)

This commit is contained in:
Brian Yencho
2024-01-31 16:28:39 -06:00
committed by Álison Fernandes
parent bafebb46f3
commit 4f08d5ddbe
8 changed files with 363 additions and 36 deletions

View File

@@ -1,8 +1,19 @@
package com.x8bit.bitwarden.data.platform.manager.util
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
/**
* Returns [AutofillSaveItem] when contained in the given [SpecialCircumstance].
*/
fun SpecialCircumstance.toAutofillSaveItemOrNull(): AutofillSaveItem? =
when (this) {
is SpecialCircumstance.AutofillSave -> this.autofillSaveItem
is SpecialCircumstance.AutofillSelection -> null
is SpecialCircumstance.ShareNewSend -> null
}
/**
* Returns [AutofillSelectionData] when contained in the given [SpecialCircumstance].
*/

View File

@@ -34,9 +34,12 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
import com.x8bit.bitwarden.ui.platform.components.BitwardenTwoButtonDialog
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.NavigationIcon
import com.x8bit.bitwarden.ui.platform.components.OverflowMenuItemData
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
import com.x8bit.bitwarden.ui.platform.theme.LocalExitManager
import com.x8bit.bitwarden.ui.platform.theme.LocalIntentManager
import com.x8bit.bitwarden.ui.platform.theme.LocalPermissionsManager
import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull
@@ -58,6 +61,7 @@ fun VaultAddEditScreen(
viewModel: VaultAddEditViewModel = hiltViewModel(),
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
intentManager: IntentManager = LocalIntentManager.current,
exitManager: ExitManager = LocalExitManager.current,
onNavigateToManualCodeEntryScreen: () -> Unit,
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
onNavigateToAttachments: (cipherId: String) -> Unit,
@@ -94,6 +98,7 @@ fun VaultAddEditScreen(
onNavigateToMoveToOrganization(event.cipherId, true)
}
VaultAddEditEvent.ExitApp -> exitManager.exitApplication()
VaultAddEditEvent.NavigateBack -> onNavigateBack.invoke()
is VaultAddEditEvent.NavigateToTooltipUri -> {
@@ -156,11 +161,14 @@ fun VaultAddEditScreen(
topBar = {
BitwardenTopAppBar(
title = state.screenDisplayName(),
navigationIcon = painterResource(id = R.drawable.ic_close),
navigationIconContentDescription = stringResource(id = R.string.close),
onNavigationIconClick = remember(viewModel) {
{ viewModel.trySendAction(VaultAddEditAction.Common.CloseClick) }
},
navigationIcon = NavigationIcon(
navigationIcon = painterResource(id = R.drawable.ic_close),
navigationIconContentDescription = stringResource(id = R.string.close),
onNavigationIconClick = remember(viewModel) {
{ viewModel.trySendAction(VaultAddEditAction.Common.CloseClick) }
},
)
.takeIf { state.shouldShowCloseButton },
scrollBehavior = scrollBehavior,
actions = {
BitwardenTextButton(

View File

@@ -8,10 +8,10 @@ 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.auth.repository.model.UserState
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.manager.util.toAutofillSaveItemOrNull
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull
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
@@ -84,16 +84,16 @@ class VaultAddEditViewModel @Inject constructor(
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 autofillSaveItem = specialCircumstanceManager
.specialCircumstance
?.toAutofillSaveItemOrNull()
val autofillSelectionData = specialCircumstanceManager
.specialCircumstance
?.toAutofillSelectionDataOrNull()
val defaultAddTypeContent = autofillSelectionData
?.toDefaultAddTypeContent()
?: autofillSaveItem
?.toDefaultAddTypeContent()
?: VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(),
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
@@ -107,6 +107,9 @@ class VaultAddEditViewModel @Inject constructor(
is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading
},
dialog = null,
// Set special conditions for autofill save
shouldShowCloseButton = autofillSaveItem == null,
shouldExitOnSave = autofillSaveItem != null,
)
},
) {
@@ -985,9 +988,16 @@ class VaultAddEditViewModel @Inject constructor(
}
is CreateCipherResult.Success -> {
sendEvent(
event = VaultAddEditEvent.NavigateBack,
)
if (state.shouldExitOnSave) {
specialCircumstanceManager.specialCircumstance = null
sendEvent(
event = VaultAddEditEvent.ExitApp,
)
} else {
sendEvent(
event = VaultAddEditEvent.NavigateBack,
)
}
}
}
}
@@ -1064,6 +1074,10 @@ class VaultAddEditViewModel @Inject constructor(
}
DataState.Loading -> {
// Skip loading states for add modes, since this will blow away any initial content
// or user-selected content.
if (state.isAddItemMode) return
mutableStateFlow.update {
it.copy(viewState = VaultAddEditState.ViewState.Loading)
}
@@ -1354,6 +1368,9 @@ data class VaultAddEditState(
val vaultAddEditType: VaultAddEditType,
val viewState: ViewState,
val dialog: DialogState?,
val shouldShowCloseButton: Boolean = true,
// Internal
val shouldExitOnSave: Boolean = false,
) : Parcelable {
/**
@@ -1704,6 +1721,11 @@ sealed class VaultAddEditEvent {
*/
data class ShowToast(val message: Text) : VaultAddEditEvent()
/**
* Leave the application.
*/
data object ExitApp : VaultAddEditEvent()
/**
* Navigate back to previous screen.
*/

View File

@@ -0,0 +1,51 @@
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
import com.x8bit.bitwarden.ui.platform.base.util.toHostOrPathOrNull
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
import java.util.UUID
/**
* Returns pre-filled content that may be used for an "add" type
* [VaultAddEditState.ViewState.Content].
*/
fun AutofillSaveItem.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content =
when (this) {
is AutofillSaveItem.Card -> {
VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(),
type = VaultAddEditState.ViewState.Content.ItemType.Card(
number = this.number.orEmpty(),
expirationMonth = VaultCardExpirationMonth
.entries
.find { it.number == this.expirationMonth }
?: VaultCardExpirationMonth.SELECT,
expirationYear = this.expirationYear.orEmpty(),
securityCode = this.securityCode.orEmpty(),
),
)
}
is AutofillSaveItem.Login -> {
val uri = this.uri
val simpleUri = uri?.toHostOrPathOrNull()
VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
name = simpleUri.orEmpty(),
),
type = VaultAddEditState.ViewState.Content.ItemType.Login(
username = this.username.orEmpty(),
password = this.password.orEmpty(),
uriList = listOf(
UriItem(
id = UUID.randomUUID().toString(),
uri = uri,
match = null,
),
),
),
)
}
}