mirror of
https://github.com/bitwarden/android.git
synced 2024-11-24 02:15:53 +03:00
PM-13068 Navigate from settings to setup autofill screen. (#4034)
This commit is contained in:
parent
bc057932a0
commit
641a48fe44
18 changed files with 301 additions and 44 deletions
|
@ -1,29 +1,79 @@
|
|||
package com.x8bit.bitwarden.ui.auth.feature.accountsetup
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.navArgument
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithPushTransitions
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
|
||||
/**
|
||||
* Route name for [SetupAutoFillScreen].
|
||||
* Route constant for navigating to the [SetupAutoFillScreen].
|
||||
*/
|
||||
const val SETUP_AUTO_FILL_ROUTE = "setup_auto_fill"
|
||||
private const val SETUP_AUTO_FILL_PREFIX = "setup_auto_fill"
|
||||
private const val SETUP_AUTO_FILL_AS_ROOT_PREFIX = "${SETUP_AUTO_FILL_PREFIX}_as_root"
|
||||
private const val SETUP_AUTO_FILL_NAV_ARG = "isInitialSetup"
|
||||
private const val SETUP_AUTO_FILL_ROUTE = "$SETUP_AUTO_FILL_PREFIX/{$SETUP_AUTO_FILL_NAV_ARG}"
|
||||
const val SETUP_AUTO_FILL_AS_ROOT_ROUTE =
|
||||
"$SETUP_AUTO_FILL_AS_ROOT_PREFIX/{$SETUP_AUTO_FILL_NAV_ARG}"
|
||||
|
||||
/**
|
||||
* Arguments for the [SetupAutoFillScreen] using [SavedStateHandle].
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
data class SetupAutoFillScreenArgs(val isInitialSetup: Boolean) {
|
||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||
isInitialSetup = requireNotNull(savedStateHandle[SETUP_AUTO_FILL_NAV_ARG]),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the setup auto-fill screen.
|
||||
*/
|
||||
fun NavController.navigateToSetupAutoFillScreen(navOptions: NavOptions? = null) {
|
||||
this.navigate(SETUP_AUTO_FILL_ROUTE, navOptions)
|
||||
this.navigate("$SETUP_AUTO_FILL_PREFIX/false", navOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the setup auto-fill screen as the root.
|
||||
*/
|
||||
fun NavController.navigateToSetupAutoFillAsRootScreen(navOptions: NavOptions? = null) {
|
||||
this.navigate("$SETUP_AUTO_FILL_AS_ROOT_PREFIX/true", navOptions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the setup auto-fil screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.setupAutoFillDestination() {
|
||||
composableWithPushTransitions(
|
||||
fun NavGraphBuilder.setupAutoFillDestination(onNavigateBack: () -> Unit) {
|
||||
composableWithSlideTransitions(
|
||||
route = SETUP_AUTO_FILL_ROUTE,
|
||||
arguments = setupAutofillNavArgs,
|
||||
) {
|
||||
SetupAutoFillScreen()
|
||||
SetupAutoFillScreen(onNavigateBack = onNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the setup autofil screen to the root nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.setupAutoFillDestinationAsRoot() {
|
||||
composableWithPushTransitions(
|
||||
route = SETUP_AUTO_FILL_AS_ROOT_ROUTE,
|
||||
arguments = setupAutofillNavArgs,
|
||||
) {
|
||||
SetupAutoFillScreen(
|
||||
onNavigateBack = {
|
||||
// No-Op
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val setupAutofillNavArgs = listOf(
|
||||
navArgument(SETUP_AUTO_FILL_NAV_ARG) {
|
||||
type = NavType.BoolType
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.x8bit.bitwarden.ui.auth.feature.accountsetup
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
|
@ -10,20 +12,31 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_STATE = "state"
|
||||
|
||||
/**
|
||||
* View model for the Auto-fill setup screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class SetupAutoFillViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val authRepository: AuthRepository,
|
||||
) :
|
||||
BaseViewModel<SetupAutoFillState, SetupAutoFillEvent, SetupAutoFillAction>(
|
||||
initialState = run {
|
||||
// We load the state from the savedStateHandle for testing purposes.
|
||||
initialState = savedStateHandle[KEY_STATE] ?: run {
|
||||
val userId = requireNotNull(authRepository.userStateFlow.value).activeUserId
|
||||
SetupAutoFillState(userId = userId, dialogState = null, autofillEnabled = false)
|
||||
val isInitialSetup = SetupAutoFillScreenArgs(savedStateHandle).isInitialSetup
|
||||
SetupAutoFillState(
|
||||
userId = userId,
|
||||
dialogState = null,
|
||||
autofillEnabled = false,
|
||||
isInitialSetup = isInitialSetup,
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
||||
|
@ -48,9 +61,15 @@ class SetupAutoFillViewModel @Inject constructor(
|
|||
is SetupAutoFillAction.Internal.AutofillEnabledUpdateReceive -> {
|
||||
handleAutofillEnabledUpdateReceive(action)
|
||||
}
|
||||
|
||||
SetupAutoFillAction.CloseClick -> handleCloseClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCloseClick() {
|
||||
sendEvent(SetupAutoFillEvent.NavigateBack)
|
||||
}
|
||||
|
||||
private fun handleAutofillEnabledUpdateReceive(
|
||||
action: SetupAutoFillAction.Internal.AutofillEnabledUpdateReceive,
|
||||
) {
|
||||
|
@ -83,7 +102,11 @@ class SetupAutoFillViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleContinueClick() {
|
||||
updateOnboardingStatusToNextStep()
|
||||
if (state.isInitialSetup) {
|
||||
updateOnboardingStatusToNextStep()
|
||||
} else {
|
||||
sendEvent(SetupAutoFillEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAutofillServiceChanged(action: SetupAutoFillAction.AutofillServiceChanged) {
|
||||
|
@ -105,24 +128,28 @@ class SetupAutoFillViewModel @Inject constructor(
|
|||
/**
|
||||
* UI State for the Auto-fill setup screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class SetupAutoFillState(
|
||||
val userId: String,
|
||||
val dialogState: SetupAutoFillDialogState?,
|
||||
val autofillEnabled: Boolean,
|
||||
)
|
||||
val isInitialSetup: Boolean,
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* Dialog states for the Auto-fill setup screen.
|
||||
*/
|
||||
sealed class SetupAutoFillDialogState {
|
||||
sealed class SetupAutoFillDialogState : Parcelable {
|
||||
/**
|
||||
* Represents the turn on later dialog.
|
||||
*/
|
||||
@Parcelize
|
||||
data object TurnOnLaterDialog : SetupAutoFillDialogState()
|
||||
|
||||
/**
|
||||
* Represents the autofill fallback dialog.
|
||||
*/
|
||||
@Parcelize
|
||||
data object AutoFillFallbackDialog : SetupAutoFillDialogState()
|
||||
}
|
||||
|
||||
|
@ -135,6 +162,11 @@ sealed class SetupAutoFillEvent {
|
|||
* Navigate to the autofill settings screen.
|
||||
*/
|
||||
data object NavigateToAutofillSettings : SetupAutoFillEvent()
|
||||
|
||||
/**
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : SetupAutoFillEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,6 +205,11 @@ sealed class SetupAutoFillAction {
|
|||
*/
|
||||
data object AutoFillServiceFallback : SetupAutoFillAction()
|
||||
|
||||
/**
|
||||
* The user has clicked the close button.
|
||||
*/
|
||||
data object CloseClick : SetupAutoFillAction()
|
||||
|
||||
/**
|
||||
* Internal actions not send through UI.
|
||||
*/
|
||||
|
|
|
@ -19,6 +19,7 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
@ -37,6 +38,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BasicDialogState
|
||||
|
@ -45,6 +47,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialo
|
|||
import com.x8bit.bitwarden.ui.platform.components.image.BitwardenGifImage
|
||||
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.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
@ -57,6 +60,7 @@ import com.x8bit.bitwarden.ui.platform.util.isPortrait
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SetupAutoFillScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
viewModel: SetupAutoFillViewModel = hiltViewModel(),
|
||||
) {
|
||||
|
@ -70,6 +74,8 @@ fun SetupAutoFillScreen(
|
|||
handler.sendAutoFillServiceFallback.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
SetupAutoFillEvent.NavigateBack -> onNavigateBack()
|
||||
}
|
||||
}
|
||||
when (state.dialogState) {
|
||||
|
@ -105,14 +111,32 @@ fun SetupAutoFillScreen(
|
|||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.account_setup),
|
||||
title = stringResource(
|
||||
id = if (state.isInitialSetup) {
|
||||
R.string.account_setup
|
||||
} else {
|
||||
R.string.turn_on_autofill
|
||||
},
|
||||
),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = null,
|
||||
navigationIcon = if (state.isInitialSetup) {
|
||||
null
|
||||
} else {
|
||||
NavigationIcon(
|
||||
navigationIcon = rememberVectorPainter(id = R.drawable.ic_close),
|
||||
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{
|
||||
viewModel.trySendAction(SetupAutoFillAction.CloseClick)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
SetupAutoFillContent(
|
||||
autofillEnabled = state.autofillEnabled,
|
||||
state = state,
|
||||
onAutofillServiceChanged = { handler.onAutofillServiceChanged(it) },
|
||||
onContinueClick = handler.onContinueClick,
|
||||
onTurnOnLaterClick = handler.onTurnOnLaterClick,
|
||||
|
@ -127,7 +151,7 @@ fun SetupAutoFillScreen(
|
|||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun SetupAutoFillContent(
|
||||
autofillEnabled: Boolean,
|
||||
state: SetupAutoFillState,
|
||||
onAutofillServiceChanged: (Boolean) -> Unit,
|
||||
onContinueClick: () -> Unit,
|
||||
onTurnOnLaterClick: () -> Unit,
|
||||
|
@ -147,7 +171,7 @@ private fun SetupAutoFillContent(
|
|||
label = stringResource(
|
||||
R.string.autofill_services,
|
||||
),
|
||||
isChecked = autofillEnabled,
|
||||
isChecked = state.autofillEnabled,
|
||||
onCheckedChange = onAutofillServiceChanged,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
@ -162,13 +186,15 @@ private fun SetupAutoFillContent(
|
|||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
BitwardenTextButton(
|
||||
label = stringResource(R.string.turn_on_later),
|
||||
onClick = onTurnOnLaterClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
if (state.isInitialSetup) {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(R.string.turn_on_later),
|
||||
onClick = onTurnOnLaterClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +266,12 @@ private fun OrderedHeaderContent() {
|
|||
private fun SetupAutoFillContentDisabled_preview() {
|
||||
BitwardenTheme {
|
||||
SetupAutoFillContent(
|
||||
autofillEnabled = false,
|
||||
state = SetupAutoFillState(
|
||||
userId = "disputationi",
|
||||
dialogState = null,
|
||||
autofillEnabled = false,
|
||||
isInitialSetup = true,
|
||||
),
|
||||
onAutofillServiceChanged = {},
|
||||
onContinueClick = {},
|
||||
onTurnOnLaterClick = {},
|
||||
|
@ -253,7 +284,12 @@ private fun SetupAutoFillContentDisabled_preview() {
|
|||
private fun SetupAutoFillContentEnabled_preview() {
|
||||
BitwardenTheme {
|
||||
SetupAutoFillContent(
|
||||
autofillEnabled = true,
|
||||
state = SetupAutoFillState(
|
||||
userId = "disputationi",
|
||||
dialogState = null,
|
||||
autofillEnabled = true,
|
||||
isInitialSetup = true,
|
||||
),
|
||||
onAutofillServiceChanged = {},
|
||||
onContinueClick = {},
|
||||
onTurnOnLaterClick = {},
|
||||
|
|
|
@ -33,6 +33,7 @@ class SetupUnlockViewModel @Inject constructor(
|
|||
private val settingsRepository: SettingsRepository,
|
||||
private val biometricsEncryptionManager: BiometricsEncryptionManager,
|
||||
) : BaseViewModel<SetupUnlockState, SetupUnlockEvent, SetupUnlockAction>(
|
||||
// We load the state from the savedStateHandle for testing purposes.
|
||||
initialState = savedStateHandle[KEY_STATE] ?: run {
|
||||
val userId = requireNotNull(authRepository.userStateFlow.value).activeUserId
|
||||
val isBiometricsValid = biometricsEncryptionManager.isBiometricIntegrityValid(
|
||||
|
|
|
@ -15,13 +15,13 @@ import androidx.navigation.NavHostController
|
|||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navOptions
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.SETUP_AUTO_FILL_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.SETUP_AUTO_FILL_AS_ROOT_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.SETUP_COMPLETE_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.SETUP_UNLOCK_AS_ROOT_ROUTE
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupAutoFillScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupAutoFillAsRootScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupCompleteScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupUnlockScreenAsRoot
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupAutoFillDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupAutoFillDestinationAsRoot
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupCompleteDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupUnlockDestinationAsRoot
|
||||
import com.x8bit.bitwarden.ui.auth.feature.auth.AUTH_GRAPH_ROUTE
|
||||
|
@ -100,7 +100,7 @@ fun RootNavScreen(
|
|||
vaultUnlockedGraph(navController)
|
||||
setupDebugMenuDestination(onNavigateBack = { navController.popBackStack() })
|
||||
setupUnlockDestinationAsRoot()
|
||||
setupAutoFillDestination()
|
||||
setupAutoFillDestinationAsRoot()
|
||||
setupCompleteDestination()
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ fun RootNavScreen(
|
|||
-> VAULT_UNLOCKED_GRAPH_ROUTE
|
||||
|
||||
RootNavState.OnboardingAccountLockSetup -> SETUP_UNLOCK_AS_ROOT_ROUTE
|
||||
RootNavState.OnboardingAutoFillSetup -> SETUP_AUTO_FILL_ROUTE
|
||||
RootNavState.OnboardingAutoFillSetup -> SETUP_AUTO_FILL_AS_ROOT_ROUTE
|
||||
RootNavState.OnboardingStepsComplete -> SETUP_COMPLETE_ROUTE
|
||||
}
|
||||
val currentRoute = navController.currentDestination?.rootLevelRoute()
|
||||
|
@ -248,7 +248,7 @@ fun RootNavScreen(
|
|||
}
|
||||
|
||||
RootNavState.OnboardingAutoFillSetup -> {
|
||||
navController.navigateToSetupAutoFillScreen(rootNavOptions)
|
||||
navController.navigateToSetupAutoFillAsRootScreen(rootNavOptions)
|
||||
}
|
||||
|
||||
RootNavState.OnboardingStepsComplete -> {
|
||||
|
|
|
@ -4,7 +4,6 @@ import androidx.navigation.NavController
|
|||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navigation
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupUnlockScreen
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithRootPushTransitions
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.about.aboutDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.about.navigateToAbout
|
||||
|
@ -34,6 +33,8 @@ fun NavGraphBuilder.settingsGraph(
|
|||
onNavigateToExportVault: () -> Unit,
|
||||
onNavigateToFolders: () -> Unit,
|
||||
onNavigateToPendingRequests: () -> Unit,
|
||||
onNavigateToSetupUnlockScreen: () -> Unit,
|
||||
onNavigateToSetupAutoFillScreen: () -> Unit,
|
||||
) {
|
||||
navigation(
|
||||
startDestination = SETTINGS_ROUTE,
|
||||
|
@ -56,12 +57,13 @@ fun NavGraphBuilder.settingsGraph(
|
|||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToDeleteAccount = onNavigateToDeleteAccount,
|
||||
onNavigateToPendingRequests = onNavigateToPendingRequests,
|
||||
onNavigateToSetupUnlockScreen = { navController.navigateToSetupUnlockScreen() },
|
||||
onNavigateToSetupUnlockScreen = onNavigateToSetupUnlockScreen,
|
||||
)
|
||||
appearanceDestination(onNavigateBack = { navController.popBackStack() })
|
||||
autoFillDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToBlockAutoFillScreen = { navController.navigateToBlockAutoFillScreen() },
|
||||
onNavigateToSetupAutofill = onNavigateToSetupAutoFillScreen,
|
||||
)
|
||||
otherDestination(onNavigateBack = { navController.popBackStack() })
|
||||
vaultSettingsDestination(
|
||||
|
|
|
@ -13,6 +13,7 @@ private const val AUTO_FILL_ROUTE = "settings_auto_fill"
|
|||
fun NavGraphBuilder.autoFillDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToBlockAutoFillScreen: () -> Unit,
|
||||
onNavigateToSetupAutofill: () -> Unit,
|
||||
) {
|
||||
composableWithPushTransitions(
|
||||
route = AUTO_FILL_ROUTE,
|
||||
|
@ -20,6 +21,7 @@ fun NavGraphBuilder.autoFillDestination(
|
|||
AutoFillScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToBlockAutoFillScreen = onNavigateToBlockAutoFillScreen,
|
||||
onNavigateToSetupAutofill = onNavigateToSetupAutofill,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ fun AutoFillScreen(
|
|||
viewModel: AutoFillViewModel = hiltViewModel(),
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
onNavigateToBlockAutoFillScreen: () -> Unit,
|
||||
onNavigateToSetupAutofill: () -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
|
@ -94,6 +95,8 @@ fun AutoFillScreen(
|
|||
AutoFillEvent.NavigateToSettings -> {
|
||||
intentManager.startCredentialManagerSettings(context)
|
||||
}
|
||||
|
||||
AutoFillEvent.NavigateToSetupAutofill -> onNavigateToSetupAutofill()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ class AutoFillViewModel @Inject constructor(
|
|||
|
||||
private fun handleAutoFillActionCardCtClick() {
|
||||
dismissShowAutofillActionCard()
|
||||
// TODO PM-13068 navigate to auto fill setup screen
|
||||
sendEvent(AutoFillEvent.NavigateToSetupAutofill)
|
||||
}
|
||||
|
||||
private fun handleUpdateShowAutofillActionCard(
|
||||
|
@ -261,6 +261,11 @@ sealed class AutoFillEvent {
|
|||
data class ShowToast(
|
||||
val text: Text,
|
||||
) : AutoFillEvent()
|
||||
|
||||
/**
|
||||
* Navigates to the setup autofill screen.
|
||||
*/
|
||||
data object NavigateToSetupAutofill : AutoFillEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,10 @@ import androidx.navigation.NavController
|
|||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navigation
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupAutoFillScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.navigateToSetupUnlockScreen
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupAutoFillDestination
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupUnlockDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.search.navigateToSearch
|
||||
import com.x8bit.bitwarden.ui.platform.feature.search.searchDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.deleteaccount.deleteAccountDestination
|
||||
|
@ -98,6 +102,8 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
passwordHistoryMode = GeneratorPasswordHistoryMode.Default,
|
||||
)
|
||||
},
|
||||
onNavigateToSetupUnlockScreen = { navController.navigateToSetupUnlockScreen() },
|
||||
onNavigateToSetupAutoFillScreen = { navController.navigateToSetupAutoFillScreen() },
|
||||
)
|
||||
deleteAccountDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
|
@ -200,5 +206,15 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
attachmentDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
setupUnlockDestination(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
setupAutoFillDestination(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ fun NavGraphBuilder.vaultUnlockedNavBarDestination(
|
|||
onNavigateToFolders: () -> Unit,
|
||||
onNavigateToPendingRequests: () -> Unit,
|
||||
onNavigateToPasswordHistory: () -> Unit,
|
||||
onNavigateToSetupUnlockScreen: () -> Unit,
|
||||
onNavigateToSetupAutoFillScreen: () -> Unit,
|
||||
) {
|
||||
composableWithStayTransitions(
|
||||
route = VAULT_UNLOCKED_NAV_BAR_ROUTE,
|
||||
|
@ -53,6 +55,8 @@ fun NavGraphBuilder.vaultUnlockedNavBarDestination(
|
|||
onNavigateToFolders = onNavigateToFolders,
|
||||
onNavigateToPendingRequests = onNavigateToPendingRequests,
|
||||
onNavigateToPasswordHistory = onNavigateToPasswordHistory,
|
||||
onNavigateToSetupUnlockScreen = onNavigateToSetupUnlockScreen,
|
||||
onNavigateToSetupAutoFillScreen = onNavigateToSetupAutoFillScreen,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ import androidx.navigation.compose.NavHost
|
|||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navOptions
|
||||
import com.x8bit.bitwarden.ui.auth.feature.accountsetup.setupUnlockDestination
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.max
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toDp
|
||||
|
@ -76,6 +75,8 @@ fun VaultUnlockedNavBarScreen(
|
|||
onNavigateToFolders: () -> Unit,
|
||||
onNavigateToPendingRequests: () -> Unit,
|
||||
onNavigateToPasswordHistory: () -> Unit,
|
||||
onNavigateToSetupUnlockScreen: () -> Unit,
|
||||
onNavigateToSetupAutoFillScreen: () -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
|
||||
|
@ -133,6 +134,8 @@ fun VaultUnlockedNavBarScreen(
|
|||
settingsTabClickedAction = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultUnlockedNavBarAction.SettingsTabClick) }
|
||||
},
|
||||
onNavigateToSetupUnlockScreen = onNavigateToSetupUnlockScreen,
|
||||
onNavigateToSetupAutoFillScreen = onNavigateToSetupAutoFillScreen,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -160,6 +163,8 @@ private fun VaultUnlockedNavBarScaffold(
|
|||
navigateToFolders: () -> Unit,
|
||||
navigateToPendingRequests: () -> Unit,
|
||||
navigateToPasswordHistory: () -> Unit,
|
||||
onNavigateToSetupUnlockScreen: () -> Unit,
|
||||
onNavigateToSetupAutoFillScreen: () -> Unit,
|
||||
) {
|
||||
var shouldDimNavBar by remember { mutableStateOf(false) }
|
||||
|
||||
|
@ -235,11 +240,8 @@ private fun VaultUnlockedNavBarScaffold(
|
|||
onNavigateToExportVault = navigateToExportVault,
|
||||
onNavigateToFolders = navigateToFolders,
|
||||
onNavigateToPendingRequests = navigateToPendingRequests,
|
||||
)
|
||||
setupUnlockDestination(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavigateToSetupUnlockScreen = onNavigateToSetupUnlockScreen,
|
||||
onNavigateToSetupAutoFillScreen = onNavigateToSetupAutoFillScreen,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.ui.auth.feature.accountsetup
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
|
@ -128,6 +129,25 @@ class SetupAutoFillViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleContinueClick send NavigateBack event when not initial setup`() = runTest {
|
||||
val viewModel = createViewModel(initialState = DEFAULT_STATE.copy(isInitialSetup = false))
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SetupAutoFillAction.ContinueClick)
|
||||
assertEquals(
|
||||
SetupAutoFillEvent.NavigateBack,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
verify(exactly = 0) {
|
||||
authRepository.setOnboardingStatus(
|
||||
DEFAULT_USER_ID,
|
||||
OnboardingStatus.FINAL_STEP,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleTurnOnLaterConfirmClick sets showAutoFillSettingBadge to true`() {
|
||||
val viewModel = createViewModel()
|
||||
|
@ -140,10 +160,34 @@ class SetupAutoFillViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun createViewModel() = SetupAutoFillViewModel(
|
||||
@Test
|
||||
fun `handleClose click sends NavigateBack event`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SetupAutoFillAction.CloseClick)
|
||||
assertEquals(SetupAutoFillEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel(
|
||||
initialState: SetupAutoFillState? = null,
|
||||
) = SetupAutoFillViewModel(
|
||||
savedStateHandle = SavedStateHandle(
|
||||
mapOf(
|
||||
"state" to initialState,
|
||||
"isInitialSetup" to true,
|
||||
),
|
||||
),
|
||||
settingsRepository = settingsRepository,
|
||||
authRepository = authRepository,
|
||||
)
|
||||
}
|
||||
|
||||
private const val DEFAULT_USER_ID = "userId"
|
||||
|
||||
private val DEFAULT_STATE = SetupAutoFillState(
|
||||
userId = DEFAULT_USER_ID,
|
||||
dialogState = null,
|
||||
autofillEnabled = false,
|
||||
isInitialSetup = true,
|
||||
)
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.compose.ui.test.filterToOne
|
|||
import androidx.compose.ui.test.hasAnyAncestor
|
||||
import androidx.compose.ui.test.isDialog
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
|
@ -15,12 +16,14 @@ import com.x8bit.bitwarden.ui.util.assertNoDialogExists
|
|||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class SetupAutofillScreenTest : BaseComposeTest() {
|
||||
private var onNavigateBackCalled = false
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<SetupAutoFillEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
|
@ -38,6 +41,7 @@ class SetupAutofillScreenTest : BaseComposeTest() {
|
|||
SetupAutoFillScreen(
|
||||
intentManager = intentManager,
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +94,15 @@ class SetupAutofillScreenTest : BaseComposeTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Turn on later component should not be displayed when not in initial setup`() {
|
||||
mutableStateFlow.update { it.copy(isInitialSetup = false) }
|
||||
composeTestRule.assertNoDialogExists()
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Turn on later")
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateToAutoFillSettings should start system autofill settings activity`() {
|
||||
every { intentManager.startSystemAutofillSettingsActivity() } returns true
|
||||
|
@ -207,10 +220,35 @@ class SetupAutofillScreenTest : BaseComposeTest() {
|
|||
}
|
||||
composeTestRule.assertNoDialogExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateBack event should invoke onNavigateBack`() {
|
||||
mutableEventFlow.tryEmit(SetupAutoFillEvent.NavigateBack)
|
||||
assertTrue(onNavigateBackCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `close icon should not show when in initial setup`() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Close")
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `close icon should show when not initial setup and send action when clicked`() {
|
||||
mutableStateFlow.update { it.copy(isInitialSetup = false) }
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Close")
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
|
||||
verify { viewModel.trySendAction(SetupAutoFillAction.CloseClick) }
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE = SetupAutoFillState(
|
||||
userId = "userId",
|
||||
dialogState = null,
|
||||
autofillEnabled = false,
|
||||
isInitialSetup = true,
|
||||
)
|
||||
|
|
|
@ -250,7 +250,7 @@ class RootNavScreenTest : BaseComposeTest() {
|
|||
RootNavState.OnboardingAutoFillSetup
|
||||
composeTestRule.runOnIdle {
|
||||
fakeNavHostController.assertLastNavigation(
|
||||
route = "setup_auto_fill",
|
||||
route = "setup_auto_fill_as_root/true",
|
||||
navOptions = expectedNavOptions,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ class AutoFillScreenTest : BaseComposeTest() {
|
|||
private var isSystemSettingsRequestSuccess = false
|
||||
private var onNavigateBackCalled = false
|
||||
private var onNavigateToBlockAutoFillScreenCalled = false
|
||||
private var onNavigateToSetupAutoFillScreenCalled = false
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<AutoFillEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
|
@ -56,6 +57,7 @@ class AutoFillScreenTest : BaseComposeTest() {
|
|||
onNavigateToBlockAutoFillScreen = { onNavigateToBlockAutoFillScreenCalled = true },
|
||||
viewModel = viewModel,
|
||||
intentManager = intentManager,
|
||||
onNavigateToSetupAutofill = { onNavigateToSetupAutoFillScreenCalled = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -498,6 +500,12 @@ class AutoFillScreenTest : BaseComposeTest() {
|
|||
.performClick()
|
||||
verify { viewModel.trySendAction(AutoFillAction.DismissShowAutofillActionCard) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when NavigateToSetupAutofill event is sent should call onNavigateToSetupAutofill`() {
|
||||
mutableEventFlow.tryEmit(AutoFillEvent.NavigateToSetupAutofill)
|
||||
assertTrue(onNavigateToSetupAutoFillScreenCalled)
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE: AutoFillState = AutoFillState(
|
||||
|
|
|
@ -321,10 +321,17 @@ class AutoFillViewModelTest : BaseViewModelTest() {
|
|||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when AutoFillActionCardCtaClick action is sent should update show autofill in repository`() {
|
||||
fun `when AutoFillActionCardCtaClick action is sent should update show autofill in repository and send NavigateToSetupAutofill event`() =
|
||||
runTest {
|
||||
mutableShowAutofillActionCardFlow.update { true }
|
||||
val viewModel = createViewModel()
|
||||
viewModel.trySendAction(AutoFillAction.AutoFillActionCardCtaClick)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AutoFillAction.AutoFillActionCardCtaClick)
|
||||
assertEquals(
|
||||
AutoFillEvent.NavigateToSetupAutofill,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
verify {
|
||||
settingsRepository.storeShowAutoFillSettingBadge(
|
||||
DEFAULT_STATE.activeUserId,
|
||||
|
|
|
@ -53,6 +53,8 @@ class VaultUnlockedNavBarScreenTest : BaseComposeTest() {
|
|||
onNavigateToPendingRequests = {},
|
||||
onNavigateToSearchVault = {},
|
||||
onNavigateToSearchSend = {},
|
||||
onNavigateToSetupAutoFillScreen = {},
|
||||
onNavigateToSetupUnlockScreen = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue