mirror of
https://github.com/bitwarden/android.git
synced 2026-03-21 22:00:42 -05:00
[PM-8217] New device notice email access UI (#4400)
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.newdevicenotice
|
||||
|
||||
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.composableWithSlideTransitions
|
||||
|
||||
private const val EMAIL_ADDRESS = "email_address"
|
||||
private const val NEW_DEVICE_NOTICE_PREFIX = "new_device_notice"
|
||||
private const val NEW_DEVICE_NOTICE_EMAIL_ACCESS_ROUTE =
|
||||
"$NEW_DEVICE_NOTICE_PREFIX/{${EMAIL_ADDRESS}}"
|
||||
|
||||
/**
|
||||
* Class to retrieve new device notice email access arguments from the [SavedStateHandle].
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
data class NewDeviceNoticeEmailAccessArgs(val emailAddress: String) {
|
||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||
checkNotNull(savedStateHandle[EMAIL_ADDRESS]) as String,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the new device notice email access screen.
|
||||
*/
|
||||
fun NavController.navigateToNewDeviceNoticeEmailAccess(
|
||||
emailAddress: String,
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
this.navigate(
|
||||
route = "$NEW_DEVICE_NOTICE_PREFIX/$emailAddress",
|
||||
navOptions = navOptions,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the new device notice email access screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.newDeviceNoticeEmailAccessDestination(
|
||||
onNavigateToTwoFactorOptions: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = NEW_DEVICE_NOTICE_EMAIL_ACCESS_ROUTE,
|
||||
arguments = listOf(
|
||||
navArgument(EMAIL_ADDRESS) { type = NavType.StringType },
|
||||
),
|
||||
) {
|
||||
NewDeviceNoticeEmailAccessScreen(
|
||||
onNavigateToTwoFactorOptions = onNavigateToTwoFactorOptions,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.newdevicenotice
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
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.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.ContinueClick
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.EmailAccessToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.createAnnotatedString
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
* The top level composable for the new device notice email access screen.
|
||||
*/
|
||||
@Composable
|
||||
fun NewDeviceNoticeEmailAccessScreen(
|
||||
onNavigateToTwoFactorOptions: () -> Unit,
|
||||
viewModel: NewDeviceNoticeEmailAccessViewModel = hiltViewModel(),
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
NavigateToTwoFactorOptions -> onNavigateToTwoFactorOptions()
|
||||
}
|
||||
}
|
||||
|
||||
BitwardenScaffold {
|
||||
NewDeviceNoticeEmailAccessContent(
|
||||
email = state.email,
|
||||
isEmailAccessEnabled = state.isEmailAccessEnabled,
|
||||
onEmailAccessToggleChanged = remember(viewModel) {
|
||||
{ newState ->
|
||||
viewModel.trySendAction(EmailAccessToggle(isEnabled = newState))
|
||||
}
|
||||
},
|
||||
onContinueClick = { viewModel.trySendAction(ContinueClick) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NewDeviceNoticeEmailAccessContent(
|
||||
email: String,
|
||||
isEmailAccessEnabled: Boolean,
|
||||
onEmailAccessToggleChanged: (Boolean) -> Unit,
|
||||
onContinueClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxSize()
|
||||
.verticalScroll(state = rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(104.dp))
|
||||
HeaderContent()
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
MainContent(
|
||||
email = email,
|
||||
isEmailAccessEnabled = isEmailAccessEnabled,
|
||||
onEmailAccessToggleChanged = onEmailAccessToggleChanged,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(R.string.continue_text),
|
||||
onClick = onContinueClick,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.imePadding(),
|
||||
)
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Header content containing the warning icon and title.
|
||||
*/
|
||||
@Suppress("MaxLineLength")
|
||||
@Composable
|
||||
private fun ColumnScope.HeaderContent() {
|
||||
Image(
|
||||
painter = rememberVectorPainter(id = R.drawable.warning),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(120.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.important_notice),
|
||||
style = BitwardenTheme.typography.titleMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.bitwarden_will_soon_send_a_code_to_your_account_email_to_verify_logins_from_new_devices_in_february,
|
||||
),
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The main content of the screen.
|
||||
*/
|
||||
@Composable
|
||||
private fun MainContent(
|
||||
email: String,
|
||||
isEmailAccessEnabled: Boolean,
|
||||
onEmailAccessToggleChanged: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
text = createAnnotatedString(
|
||||
mainString = stringResource(
|
||||
R.string.do_you_have_reliable_access_to_your_email,
|
||||
email,
|
||||
),
|
||||
mainStringStyle = SpanStyle(
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
fontSize = BitwardenTheme.typography.bodyLarge.fontSize,
|
||||
fontWeight = FontWeight.Normal,
|
||||
),
|
||||
highlights = listOf(email),
|
||||
highlightStyle = SpanStyle(
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
fontSize = BitwardenTheme.typography.bodyLarge.fontSize,
|
||||
fontWeight = FontWeight.Bold,
|
||||
),
|
||||
),
|
||||
)
|
||||
Column {
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.yes_i_can_reliably_access_my_email),
|
||||
isChecked = isEmailAccessEnabled,
|
||||
onCheckedChange = onEmailAccessToggleChanged,
|
||||
modifier = Modifier
|
||||
.testTag("EmailAccessToggle"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewScreenSizes
|
||||
@Composable
|
||||
private fun NewDeviceNoticeEmailAccessScreen_preview() {
|
||||
BitwardenTheme {
|
||||
NewDeviceNoticeEmailAccessContent(
|
||||
email = "test@bitwarden.com",
|
||||
isEmailAccessEnabled = true,
|
||||
onEmailAccessToggleChanged = {},
|
||||
onContinueClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.newdevicenotice
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.ContinueClick
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.EmailAccessToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_STATE = "state"
|
||||
|
||||
/**
|
||||
* Manages application state for the new device notice email access screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class NewDeviceNoticeEmailAccessViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : BaseViewModel<
|
||||
NewDeviceNoticeEmailAccessState,
|
||||
NewDeviceNoticeEmailAccessEvent,
|
||||
NewDeviceNoticeEmailAccessAction,
|
||||
>(
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
?: NewDeviceNoticeEmailAccessState(
|
||||
email = NewDeviceNoticeEmailAccessArgs(savedStateHandle).emailAddress,
|
||||
isEmailAccessEnabled = false,
|
||||
),
|
||||
) {
|
||||
override fun handleAction(action: NewDeviceNoticeEmailAccessAction) {
|
||||
when (action) {
|
||||
ContinueClick -> handleContinueClick()
|
||||
is EmailAccessToggle -> handleEmailAccessToggle(action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleContinueClick() {
|
||||
// TODO PM-8217: update new device notice status and navigate accordingly
|
||||
sendEvent(NavigateToTwoFactorOptions)
|
||||
}
|
||||
|
||||
private fun handleEmailAccessToggle(action: EmailAccessToggle) {
|
||||
mutableStateFlow.update {
|
||||
it.copy(isEmailAccessEnabled = action.isEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models state of the new device notice email access screen.
|
||||
*/
|
||||
@Parcelize
|
||||
data class NewDeviceNoticeEmailAccessState(
|
||||
val email: String,
|
||||
val isEmailAccessEnabled: Boolean,
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* Models events for the new device notice email access screen.
|
||||
*/
|
||||
sealed class NewDeviceNoticeEmailAccessEvent {
|
||||
/**
|
||||
* Navigates to the Two Factor Options screen.
|
||||
*/
|
||||
data object NavigateToTwoFactorOptions : NewDeviceNoticeEmailAccessEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the new device notice email access screen.
|
||||
*/
|
||||
sealed class NewDeviceNoticeEmailAccessAction {
|
||||
/**
|
||||
* User tapped the continue button.
|
||||
*/
|
||||
data object ContinueClick : NewDeviceNoticeEmailAccessAction()
|
||||
|
||||
/**
|
||||
* User tapped the email access toggle.
|
||||
*/
|
||||
data class EmailAccessToggle(val isEnabled: Boolean) : NewDeviceNoticeEmailAccessAction()
|
||||
}
|
||||
@@ -130,6 +130,8 @@ fun @receiver:StringRes Int.asText(vararg args: Any): Text = ResArgsText(this, a
|
||||
* Create an [AnnotatedString] with highlighted parts.
|
||||
* @param mainString the full string
|
||||
* @param highlights parts of the mainString that will be highlighted
|
||||
* @param highlightStyle the style to apply to the highlights
|
||||
* @param mainStringStyle the style to apply to the mainString
|
||||
* @param tag the tag that will be used for the annotation
|
||||
*/
|
||||
@Composable
|
||||
@@ -137,12 +139,13 @@ fun createAnnotatedString(
|
||||
mainString: String,
|
||||
highlights: List<String>,
|
||||
highlightStyle: SpanStyle = bitwardenClickableTextSpanStyle,
|
||||
mainStringStyle: SpanStyle = bitwardenDefaultSpanStyle,
|
||||
tag: String? = null,
|
||||
): AnnotatedString {
|
||||
return buildAnnotatedString {
|
||||
append(mainString)
|
||||
addStyle(
|
||||
style = bitwardenDefaultSpanStyle,
|
||||
style = mainStringStyle,
|
||||
start = 0,
|
||||
end = mainString.length,
|
||||
)
|
||||
|
||||
93
app/src/main/res/drawable-night/warning.xml
Normal file
93
app/src/main/res/drawable-night/warning.xml
Normal file
@@ -0,0 +1,93 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="96dp"
|
||||
android:height="96dp"
|
||||
android:viewportWidth="96"
|
||||
android:viewportHeight="96">
|
||||
<path
|
||||
android:pathData="M54,8a8,8 0,0 1,8 -8h8a8,8 0,0 1,8 8v4H54V8Z"
|
||||
android:fillColor="#79A1E9"/>
|
||||
<path
|
||||
android:pathData="M70,2h-8a6,6 0,0 0,-6 6v2h20L76,8a6,6 0,0 0,-6 -6ZM62,0a8,8 0,0 0,-8 8v4h24L78,8a8,8 0,0 0,-8 -8h-8Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M47,16a8,8 0,0 1,8 -8h22a8,8 0,0 1,8 8v6H47v-6Z"
|
||||
android:fillColor="#79A1E9"/>
|
||||
<path
|
||||
android:pathData="M77,10H55a6,6 0,0 0,-6 6v4h34v-4a6,6 0,0 0,-6 -6ZM55,8a8,8 0,0 0,-8 8v6h38v-6a8,8 0,0 0,-8 -8H55Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M40,26a8,8 0,0 1,8 -8h36a8,8 0,0 1,8 8v66H40V26Z"
|
||||
android:fillColor="#79A1E9"/>
|
||||
<path
|
||||
android:pathData="M84,20L48,20a6,6 0,0 0,-6 6v64h48L90,26a6,6 0,0 0,-6 -6ZM48,18a8,8 0,0 0,-8 8v66h52L92,26a8,8 0,0 0,-8 -8L48,18Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M4,44a8,8 0,0 1,8 -8h38a8,8 0,0 1,8 8v48H4V44Z"
|
||||
android:fillColor="#AAC3EF"/>
|
||||
<path
|
||||
android:pathData="M50,38L12,38a6,6 0,0 0,-6 6v46h50L56,44a6,6 0,0 0,-6 -6ZM12,36a8,8 0,0 0,-8 8v48h54L58,44a8,8 0,0 0,-8 -8L12,36Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M68.677,60.435c1.928,-3.316 6.718,-3.316 8.645,0l16.31,28.052C95.57,91.82 93.165,96 89.309,96H56.691c-3.856,0 -6.26,-4.18 -4.323,-7.513l16.31,-28.052Z"
|
||||
android:fillColor="#FFBF00"/>
|
||||
<path
|
||||
android:pathData="M91.903,89.492 L75.593,61.44c-1.156,-1.99 -4.03,-1.99 -5.187,0L54.097,89.492c-1.163,2 0.28,4.508 2.594,4.508h32.618c2.314,0 3.757,-2.508 2.594,-4.508ZM77.323,60.435c-1.928,-3.316 -6.718,-3.316 -8.645,0l-16.31,28.052C50.43,91.82 52.835,96 56.691,96h32.618c3.856,0 6.26,-4.18 4.323,-7.513l-16.31,-28.052Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M75,88a2,2 0,1 1,-4 0,2 2,0 0,1 4,0ZM70.06,70.553a0.5,0.5 0,0 1,0.496 -0.553h4.888a0.5,0.5 0,0 1,0.497 0.553l-1.393,13a0.5,0.5 0,0 1,-0.497 0.447h-2.102a0.5,0.5 0,0 1,-0.497 -0.447l-1.393,-13Z"
|
||||
android:fillColor="#175DDC"/>
|
||||
<path
|
||||
android:pathData="M21,80a8,8 0,0 1,8 -8h4a8,8 0,0 1,8 8v12H21V80Z"
|
||||
android:fillColor="#79A1E9"/>
|
||||
<path
|
||||
android:pathData="M33,74h-4a6,6 0,0 0,-6 6v10h16L39,80a6,6 0,0 0,-6 -6ZM29,72a8,8 0,0 0,-8 8v12h20L41,80a8,8 0,0 0,-8 -8h-4Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M13,46a1,1 0,0 1,1 -1h8a1,1 0,0 1,1 1v8a1,1 0,0 1,-1 1h-8a1,1 0,0 1,-1 -1v-8Z"
|
||||
android:fillColor="#F3F6F9"/>
|
||||
<path
|
||||
android:pathData="M15,47v6h6v-6h-6ZM14,45a1,1 0,0 0,-1 1v8a1,1 0,0 0,1 1h8a1,1 0,0 0,1 -1v-8a1,1 0,0 0,-1 -1h-8Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M13,59a1,1 0,0 1,1 -1h8a1,1 0,0 1,1 1v8a1,1 0,0 1,-1 1h-8a1,1 0,0 1,-1 -1v-8Z"
|
||||
android:fillColor="#F3F6F9"/>
|
||||
<path
|
||||
android:pathData="M15,60v6h6v-6h-6ZM14,58a1,1 0,0 0,-1 1v8a1,1 0,0 0,1 1h8a1,1 0,0 0,1 -1v-8a1,1 0,0 0,-1 -1h-8Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M26,46a1,1 0,0 1,1 -1h8a1,1 0,0 1,1 1v8a1,1 0,0 1,-1 1h-8a1,1 0,0 1,-1 -1v-8Z"
|
||||
android:fillColor="#F3F6F9"/>
|
||||
<path
|
||||
android:pathData="M28,47v6h6v-6h-6ZM27,45a1,1 0,0 0,-1 1v8a1,1 0,0 0,1 1h8a1,1 0,0 0,1 -1v-8a1,1 0,0 0,-1 -1h-8Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M26,59a1,1 0,0 1,1 -1h8a1,1 0,0 1,1 1v8a1,1 0,0 1,-1 1h-8a1,1 0,0 1,-1 -1v-8Z"
|
||||
android:fillColor="#F3F6F9"/>
|
||||
<path
|
||||
android:pathData="M28,60v6h6v-6h-6ZM27,58a1,1 0,0 0,-1 1v8a1,1 0,0 0,1 1h8a1,1 0,0 0,1 -1v-8a1,1 0,0 0,-1 -1h-8Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M39,59a1,1 0,0 1,1 -1h8a1,1 0,0 1,1 1v8a1,1 0,0 1,-1 1h-8a1,1 0,0 1,-1 -1v-8Z"
|
||||
android:fillColor="#F3F6F9"/>
|
||||
<path
|
||||
android:pathData="M41,60v6h6v-6h-6ZM40,58a1,1 0,0 0,-1 1v8a1,1 0,0 0,1 1h8a1,1 0,0 0,1 -1v-8a1,1 0,0 0,-1 -1h-8Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M39,46a1,1 0,0 1,1 -1h8a1,1 0,0 1,1 1v8a1,1 0,0 1,-1 1h-8a1,1 0,0 1,-1 -1v-8Z"
|
||||
android:fillColor="#F3F6F9"/>
|
||||
<path
|
||||
android:pathData="M41,47v6h6v-6h-6ZM40,45a1,1 0,0 0,-1 1v8a1,1 0,0 0,1 1h8a1,1 0,0 0,1 -1v-8a1,1 0,0 0,-1 -1h-8ZM47,27a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM58,26a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6ZM67,27a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM77,27a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM77,33a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM78,38a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6ZM77,45a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM78,50a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6ZM68,32a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6ZM67,39a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM68,44a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6ZM67,51a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM58,32a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6ZM47,33a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM58,38a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6ZM57,45a1,1 0,0 1,1 -1h6a1,1 0,1 1,0 2h-6a1,1 0,0 1,-1 -1ZM58,50a1,1 0,1 0,0 2h6a1,1 0,1 0,0 -2h-6Z"
|
||||
android:fillColor="#175DDC"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
104
app/src/main/res/drawable/warning.xml
Normal file
104
app/src/main/res/drawable/warning.xml
Normal file
@@ -0,0 +1,104 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="124dp"
|
||||
android:height="124dp"
|
||||
android:viewportWidth="124"
|
||||
android:viewportHeight="124">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h124v124h-124z"/>
|
||||
<path
|
||||
android:pathData="M69.75,10.33C69.75,4.63 74.38,0 80.08,0H90.42C96.12,0 100.75,4.63 100.75,10.33V15.5H69.75V10.33Z"
|
||||
android:fillColor="#AAC3EF"/>
|
||||
<path
|
||||
android:pathData="M90.42,2.58H80.08C75.8,2.58 72.33,6.05 72.33,10.33V12.92H98.17V10.33C98.17,6.05 94.7,2.58 90.42,2.58ZM80.08,0C74.38,0 69.75,4.63 69.75,10.33V15.5H100.75V10.33C100.75,4.63 96.12,0 90.42,0H80.08Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M60.71,20.67C60.71,14.96 65.33,10.33 71.04,10.33H99.46C105.17,10.33 109.79,14.96 109.79,20.67V28.42H60.71V20.67Z"
|
||||
android:fillColor="#AAC3EF"/>
|
||||
<path
|
||||
android:pathData="M99.46,12.92H71.04C66.76,12.92 63.29,16.39 63.29,20.67V25.83H107.21V20.67C107.21,16.39 103.74,12.92 99.46,12.92ZM71.04,10.33C65.33,10.33 60.71,14.96 60.71,20.67V28.42H109.79V20.67C109.79,14.96 105.17,10.33 99.46,10.33H71.04Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M51.67,33.58C51.67,27.88 56.29,23.25 62,23.25H108.5C114.21,23.25 118.83,27.88 118.83,33.58V118.83H51.67V33.58Z"
|
||||
android:fillColor="#AAC3EF"/>
|
||||
<path
|
||||
android:pathData="M108.5,25.83H62C57.72,25.83 54.25,29.3 54.25,33.58V116.25H116.25V33.58C116.25,29.3 112.78,25.83 108.5,25.83ZM62,23.25C56.29,23.25 51.67,27.88 51.67,33.58V118.83H118.83V33.58C118.83,27.88 114.21,23.25 108.5,23.25H62Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M5.17,56.83C5.17,51.13 9.79,46.5 15.5,46.5H64.58C70.29,46.5 74.92,51.13 74.92,56.83V118.83H5.17V56.83Z"
|
||||
android:fillColor="#DBE5F6"/>
|
||||
<path
|
||||
android:pathData="M64.58,49.08H15.5C11.22,49.08 7.75,52.55 7.75,56.83V116.25H72.33V56.83C72.33,52.55 68.86,49.08 64.58,49.08ZM15.5,46.5C9.79,46.5 5.17,51.13 5.17,56.83V118.83H74.92V56.83C74.92,51.13 70.29,46.5 64.58,46.5H15.5Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M88.71,78.06C91.2,73.78 97.38,73.78 99.87,78.06L120.94,114.29C123.44,118.6 120.34,124 115.36,124H73.23C68.25,124 65.14,118.6 67.64,114.29L88.71,78.06Z"
|
||||
android:fillColor="#FFBF00"/>
|
||||
<path
|
||||
android:pathData="M118.71,115.59L97.64,79.36C96.15,76.79 92.44,76.79 90.94,79.36L69.88,115.59C68.37,118.18 70.24,121.42 73.23,121.42H115.36C118.35,121.42 120.21,118.18 118.71,115.59ZM99.87,78.06C97.38,73.78 91.2,73.78 88.71,78.06L67.64,114.29C65.14,118.6 68.25,124 73.23,124H115.36C120.34,124 123.44,118.6 120.94,114.29L99.87,78.06Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M96.88,113.67C96.88,115.09 95.72,116.25 94.29,116.25C92.86,116.25 91.71,115.09 91.71,113.67C91.71,112.24 92.86,111.08 94.29,111.08C95.72,111.08 96.88,112.24 96.88,113.67Z"
|
||||
android:fillColor="#020F66"/>
|
||||
<path
|
||||
android:pathData="M90.49,91.13C90.45,90.75 90.75,90.42 91.14,90.42H97.45C97.83,90.42 98.13,90.75 98.09,91.13L96.29,107.92C96.26,108.25 95.98,108.5 95.65,108.5H92.93C92.6,108.5 92.33,108.25 92.29,107.92L90.49,91.13Z"
|
||||
android:fillColor="#020F66"/>
|
||||
<path
|
||||
android:pathData="M27.13,103.33C27.13,97.63 31.75,93 37.46,93H42.63C48.33,93 52.96,97.63 52.96,103.33V118.83H27.13V103.33Z"
|
||||
android:fillColor="#AAC3EF"/>
|
||||
<path
|
||||
android:pathData="M42.63,95.58H37.46C33.18,95.58 29.71,99.05 29.71,103.33V116.25H50.38V103.33C50.38,99.05 46.91,95.58 42.63,95.58ZM37.46,93C31.75,93 27.13,97.63 27.13,103.33V118.83H52.96V103.33C52.96,97.63 48.33,93 42.63,93H37.46Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M16.79,59.42C16.79,58.7 17.37,58.13 18.08,58.13H28.42C29.13,58.13 29.71,58.7 29.71,59.42V69.75C29.71,70.46 29.13,71.04 28.42,71.04H18.08C17.37,71.04 16.79,70.46 16.79,69.75V59.42Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M19.38,60.71V68.46H27.13V60.71H19.38ZM18.08,58.13C17.37,58.13 16.79,58.7 16.79,59.42V69.75C16.79,70.46 17.37,71.04 18.08,71.04H28.42C29.13,71.04 29.71,70.46 29.71,69.75V59.42C29.71,58.7 29.13,58.13 28.42,58.13H18.08Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M16.79,76.21C16.79,75.49 17.37,74.92 18.08,74.92H28.42C29.13,74.92 29.71,75.49 29.71,76.21V86.54C29.71,87.25 29.13,87.83 28.42,87.83H18.08C17.37,87.83 16.79,87.25 16.79,86.54V76.21Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M19.38,77.5V85.25H27.13V77.5H19.38ZM18.08,74.92C17.37,74.92 16.79,75.49 16.79,76.21V86.54C16.79,87.25 17.37,87.83 18.08,87.83H28.42C29.13,87.83 29.71,87.25 29.71,86.54V76.21C29.71,75.49 29.13,74.92 28.42,74.92H18.08Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M33.58,59.42C33.58,58.7 34.16,58.13 34.88,58.13H45.21C45.92,58.13 46.5,58.7 46.5,59.42V69.75C46.5,70.46 45.92,71.04 45.21,71.04H34.88C34.16,71.04 33.58,70.46 33.58,69.75V59.42Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M36.17,60.71V68.46H43.92V60.71H36.17ZM34.88,58.13C34.16,58.13 33.58,58.7 33.58,59.42V69.75C33.58,70.46 34.16,71.04 34.88,71.04H45.21C45.92,71.04 46.5,70.46 46.5,69.75V59.42C46.5,58.7 45.92,58.13 45.21,58.13H34.88Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M33.58,76.21C33.58,75.49 34.16,74.92 34.88,74.92H45.21C45.92,74.92 46.5,75.49 46.5,76.21V86.54C46.5,87.25 45.92,87.83 45.21,87.83H34.88C34.16,87.83 33.58,87.25 33.58,86.54V76.21Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M36.17,77.5V85.25H43.92V77.5H36.17ZM34.88,74.92C34.16,74.92 33.58,75.49 33.58,76.21V86.54C33.58,87.25 34.16,87.83 34.88,87.83H45.21C45.92,87.83 46.5,87.25 46.5,86.54V76.21C46.5,75.49 45.92,74.92 45.21,74.92H34.88Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M50.38,76.21C50.38,75.49 50.95,74.92 51.67,74.92H62C62.71,74.92 63.29,75.49 63.29,76.21V86.54C63.29,87.25 62.71,87.83 62,87.83H51.67C50.95,87.83 50.38,87.25 50.38,86.54V76.21Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M52.96,77.5V85.25H60.71V77.5H52.96ZM51.67,74.92C50.95,74.92 50.38,75.49 50.38,76.21V86.54C50.38,87.25 50.95,87.83 51.67,87.83H62C62.71,87.83 63.29,87.25 63.29,86.54V76.21C63.29,75.49 62.71,74.92 62,74.92H51.67Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M50.38,59.42C50.38,58.7 50.95,58.13 51.67,58.13H62C62.71,58.13 63.29,58.7 63.29,59.42V69.75C63.29,70.46 62.71,71.04 62,71.04H51.67C50.95,71.04 50.38,70.46 50.38,69.75V59.42Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M52.96,60.71V68.46H60.71V60.71H52.96ZM51.67,58.13C50.95,58.13 50.38,58.7 50.38,59.42V69.75C50.38,70.46 50.95,71.04 51.67,71.04H62C62.71,71.04 63.29,70.46 63.29,69.75V59.42C63.29,58.7 62.71,58.13 62,58.13H51.67Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M60.71,34.88C60.71,34.16 61.29,33.58 62,33.58H69.75C70.46,33.58 71.04,34.16 71.04,34.88C71.04,35.59 70.46,36.17 69.75,36.17L62,36.17C61.29,36.17 60.71,35.59 60.71,34.88ZM74.92,33.58C74.2,33.58 73.63,34.16 73.63,34.88C73.63,35.59 74.2,36.17 74.92,36.17L82.67,36.17C83.38,36.17 83.96,35.59 83.96,34.88C83.96,34.16 83.38,33.58 82.67,33.58H74.92ZM86.54,34.88C86.54,34.16 87.12,33.58 87.83,33.58H95.58C96.3,33.58 96.88,34.16 96.88,34.88C96.88,35.59 96.3,36.17 95.58,36.17L87.83,36.17C87.12,36.17 86.54,35.59 86.54,34.88ZM99.46,34.88C99.46,34.16 100.04,33.58 100.75,33.58H108.5C109.21,33.58 109.79,34.16 109.79,34.88C109.79,35.59 109.21,36.17 108.5,36.17L100.75,36.17C100.04,36.17 99.46,35.59 99.46,34.88ZM99.46,42.63C99.46,41.91 100.04,41.33 100.75,41.33L108.5,41.33C109.21,41.33 109.79,41.91 109.79,42.63C109.79,43.34 109.21,43.92 108.5,43.92L100.75,43.92C100.04,43.92 99.46,43.34 99.46,42.63ZM100.75,49.08C100.04,49.08 99.46,49.66 99.46,50.38C99.46,51.09 100.04,51.67 100.75,51.67H108.5C109.21,51.67 109.79,51.09 109.79,50.38C109.79,49.66 109.21,49.08 108.5,49.08L100.75,49.08ZM99.46,58.13C99.46,57.41 100.04,56.83 100.75,56.83H108.5C109.21,56.83 109.79,57.41 109.79,58.13C109.79,58.84 109.21,59.42 108.5,59.42H100.75C100.04,59.42 99.46,58.84 99.46,58.13ZM100.75,64.58C100.04,64.58 99.46,65.16 99.46,65.88C99.46,66.59 100.04,67.17 100.75,67.17H108.5C109.21,67.17 109.79,66.59 109.79,65.88C109.79,65.16 109.21,64.58 108.5,64.58H100.75ZM87.83,41.33C87.12,41.33 86.54,41.91 86.54,42.63C86.54,43.34 87.12,43.92 87.83,43.92L95.58,43.92C96.3,43.92 96.88,43.34 96.88,42.63C96.88,41.91 96.3,41.33 95.58,41.33L87.83,41.33ZM86.54,50.38C86.54,49.66 87.12,49.08 87.83,49.08L95.58,49.08C96.3,49.08 96.88,49.66 96.88,50.38C96.88,51.09 96.3,51.67 95.58,51.67H87.83C87.12,51.67 86.54,51.09 86.54,50.38ZM87.83,56.83C87.12,56.83 86.54,57.41 86.54,58.13C86.54,58.84 87.12,59.42 87.83,59.42H95.58C96.3,59.42 96.88,58.84 96.88,58.13C96.88,57.41 96.3,56.83 95.58,56.83H87.83ZM86.54,65.88C86.54,65.16 87.12,64.58 87.83,64.58H95.58C96.3,64.58 96.88,65.16 96.88,65.88C96.88,66.59 96.3,67.17 95.58,67.17H87.83C87.12,67.17 86.54,66.59 86.54,65.88ZM74.92,41.33C74.2,41.33 73.63,41.91 73.63,42.63C73.63,43.34 74.2,43.92 74.92,43.92L82.67,43.92C83.38,43.92 83.96,43.34 83.96,42.63C83.96,41.91 83.38,41.33 82.67,41.33L74.92,41.33ZM60.71,42.63C60.71,41.91 61.29,41.33 62,41.33L69.75,41.33C70.46,41.33 71.04,41.91 71.04,42.63C71.04,43.34 70.46,43.92 69.75,43.92L62,43.92C61.29,43.92 60.71,43.34 60.71,42.63ZM74.92,49.08C74.2,49.08 73.63,49.66 73.63,50.38C73.63,51.09 74.2,51.67 74.92,51.67H82.67C83.38,51.67 83.96,51.09 83.96,50.38C83.96,49.66 83.38,49.08 82.67,49.08L74.92,49.08ZM73.63,58.13C73.63,57.41 74.2,56.83 74.92,56.83H82.67C83.38,56.83 83.96,57.41 83.96,58.13C83.96,58.84 83.38,59.42 82.67,59.42H74.92C74.2,59.42 73.63,58.84 73.63,58.13ZM74.92,64.58C74.2,64.58 73.63,65.16 73.63,65.88C73.63,66.59 74.2,67.17 74.92,67.17H82.67C83.38,67.17 83.96,66.59 83.96,65.88C83.96,65.16 83.38,64.58 82.67,64.58H74.92Z"
|
||||
android:fillColor="#020F66"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1098,6 +1098,10 @@ Do you want to switch to this account?</string>
|
||||
<string name="copy_email">Copy email</string>
|
||||
<string name="copy_phone">Copy phone number</string>
|
||||
<string name="copy_address">Copy address</string>
|
||||
<string name="important_notice">Important notice</string>
|
||||
<string name="bitwarden_will_soon_send_a_code_to_your_account_email_to_verify_logins_from_new_devices_in_february">Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025.</string>
|
||||
<string name="do_you_have_reliable_access_to_your_email">Do you have reliable access to your email, %1$s? </string>
|
||||
<string name="yes_i_can_reliably_access_my_email">Yes, I can reliably access my email</string>
|
||||
<string name="biometrics_no_longer_supported_title">Biometrics are no longer supported on this device</string>
|
||||
<string name="biometrics_no_longer_supported">You’ve been logged out because your device’s biometrics don’t meet the latest security requirements. To update settings, log in once again or contact your administrator for access.</string>
|
||||
<string name="cxp_import">CXP Import</string>
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.newdevicenotice
|
||||
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
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 NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() {
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<NewDeviceNoticeEmailAccessEvent>()
|
||||
private var onNavigateToTwoFactorOptionsCalled = false
|
||||
private val viewModel = mockk<NewDeviceNoticeEmailAccessViewModel>(relaxed = true) {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns mutableEventFlow
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
composeTestRule.setContent {
|
||||
NewDeviceNoticeEmailAccessScreen(
|
||||
onNavigateToTwoFactorOptions = { onNavigateToTwoFactorOptionsCalled = true },
|
||||
viewModel = viewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `Do you have reliable access to your email should be toggled on or off according to the state`() {
|
||||
composeTestRule
|
||||
.onNodeWithText("Yes, I can reliably access my email", substring = true)
|
||||
.assertIsOff()
|
||||
|
||||
mutableStateFlow.update { it.copy(isEmailAccessEnabled = true) }
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText("Yes, I can reliably access my email", substring = true)
|
||||
.assertIsOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Do you have reliable access to your email click should send EmailAccessToggle action`() {
|
||||
composeTestRule
|
||||
.onNodeWithText("Yes, I can reliably access my email")
|
||||
.performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(
|
||||
NewDeviceNoticeEmailAccessAction.EmailAccessToggle(true),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Continue button click should send ContinueButtonClick action`() {
|
||||
composeTestRule.onNodeWithText("Continue").performScrollTo().performClick()
|
||||
verify {
|
||||
viewModel.trySendAction(NewDeviceNoticeEmailAccessAction.ContinueClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ContinueClick should call onNavigateToTwoFactorOptions`() {
|
||||
mutableEventFlow.tryEmit(NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions)
|
||||
assertTrue(onNavigateToTwoFactorOptionsCalled)
|
||||
}
|
||||
}
|
||||
|
||||
private const val EMAIL = "active@bitwarden.com"
|
||||
|
||||
private val DEFAULT_STATE =
|
||||
NewDeviceNoticeEmailAccessState(
|
||||
email = EMAIL,
|
||||
isEmailAccessEnabled = false,
|
||||
)
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.newdevicenotice
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class NewDeviceNoticeEmailAccessViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct with email from state handle`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(DEFAULT_STATE, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `EmailAccessToggle should update value of isEmailAccessEnabled`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(NewDeviceNoticeEmailAccessAction.EmailAccessToggle(true))
|
||||
assertEquals(
|
||||
viewModel.stateFlow.value,
|
||||
DEFAULT_STATE.copy(isEmailAccessEnabled = true),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ContinueClick with valid email should emit NavigateToTwoFactorOptions`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(NewDeviceNoticeEmailAccessAction.ContinueClick)
|
||||
assertEquals(
|
||||
NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions,
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel(
|
||||
savedStateHandle: SavedStateHandle = SavedStateHandle().also {
|
||||
it["email_address"] = EMAIL
|
||||
},
|
||||
): NewDeviceNoticeEmailAccessViewModel = NewDeviceNoticeEmailAccessViewModel(
|
||||
savedStateHandle = savedStateHandle,
|
||||
)
|
||||
}
|
||||
|
||||
private const val EMAIL = "active@bitwarden.com"
|
||||
|
||||
private val DEFAULT_STATE =
|
||||
NewDeviceNoticeEmailAccessState(
|
||||
email = EMAIL,
|
||||
isEmailAccessEnabled = false,
|
||||
)
|
||||
Reference in New Issue
Block a user