diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityService.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityService.kt index 09bd21c38..5d73cace0 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityService.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityService.kt @@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson +import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson @@ -68,7 +69,7 @@ interface IdentityService { */ suspend fun sendVerificationEmail( body: SendVerificationEmailRequestJson, - ): Result + ): Result /** * Register a new account to Bitwarden using email verification flow. diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt index cb2a8e4f9..cf0efd887 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceImpl.kt @@ -11,6 +11,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson +import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson @@ -132,11 +133,20 @@ class IdentityServiceImpl( override suspend fun sendVerificationEmail( body: SendVerificationEmailRequestJson, - ): Result { + ): Result { return unauthenticatedIdentityApi .sendVerificationEmail(body = body) .toResult() - .map { it?.content } + .map { SendVerificationEmailResponseJson.Success(it?.content) } + .recoverCatching { throwable -> + throwable + .toBitwardenError() + .parseErrorBodyOrNull( + code = 400, + json = json, + ) + ?: throw throwable + } } override suspend fun verifyEmailRegistrationToken( diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index a769f88aa..ca0521540 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -22,6 +22,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJs import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson +import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod @@ -1282,7 +1283,15 @@ class AuthRepositoryImpl( ) .fold( onSuccess = { - SendVerificationEmailResult.Success(it) + when (it) { + is SendVerificationEmailResponseJson.Invalid -> { + SendVerificationEmailResult.Error(it.message) + } + + is SendVerificationEmailResponseJson.Success -> { + SendVerificationEmailResult.Success(it.emailVerificationToken) + } + } }, onFailure = { SendVerificationEmailResult.Error(null) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreen.kt index 6be4b6c75..376758126 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/checkemail/CheckEmailScreen.kt @@ -297,7 +297,7 @@ private fun CheckEmailLegacyContent( ), highlights = listOf( ClickableTextHighlight( - textToHighlight = stringResource(id = R.string.log_in), + textToHighlight = stringResource(id = R.string.log_in_verb), onTextClick = onLoginClick, ), ), diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicator.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicator.kt index abdc86b3f..7835542d4 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicator.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/completeregistration/PasswordStrengthIndicator.kt @@ -42,14 +42,20 @@ fun PasswordStrengthIndicator( currentCharacterCount: Int, minimumCharacterCount: Int? = null, ) { + val minimumRequirementMet = (minimumCharacterCount == null) || + (currentCharacterCount >= minimumCharacterCount) val widthPercent by animateFloatAsState( - targetValue = when (state) { - PasswordStrengthState.NONE -> 0f - PasswordStrengthState.WEAK_1 -> .25f - PasswordStrengthState.WEAK_2 -> .5f - PasswordStrengthState.WEAK_3 -> .66f - PasswordStrengthState.GOOD -> .82f - PasswordStrengthState.STRONG -> 1f + targetValue = if (minimumRequirementMet) { + when (state) { + PasswordStrengthState.NONE -> 0f + PasswordStrengthState.WEAK_1 -> .25f + PasswordStrengthState.WEAK_2 -> .5f + PasswordStrengthState.WEAK_3 -> .66f + PasswordStrengthState.GOOD -> .82f + PasswordStrengthState.STRONG -> 1f + } + } else { + 0f }, label = "Width Percent State", ) @@ -107,11 +113,13 @@ fun PasswordStrengthIndicator( minimumCharacterCount = minCount, ) } - Text( - text = label(), - style = BitwardenTheme.typography.labelSmall, - color = indicatorColor, - ) + if (minimumRequirementMet) { + Text( + text = label(), + style = BitwardenTheme.typography.labelSmall, + color = indicatorColor, + ) + } } } } @@ -122,14 +130,6 @@ private fun MinimumCharacterCount( minimumRequirementMet: Boolean, minimumCharacterCount: Int, ) { - val characterCountColor by animateColorAsState( - targetValue = if (minimumRequirementMet) { - BitwardenTheme.colorScheme.status.strong - } else { - BitwardenTheme.colorScheme.text.secondary - }, - label = "minmumCharacterCountColor", - ) Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, @@ -145,14 +145,14 @@ private fun MinimumCharacterCount( Icon( painter = rememberVectorPainter(id = it), contentDescription = null, - tint = characterCountColor, + tint = BitwardenTheme.colorScheme.text.secondary, modifier = Modifier.size(12.dp), ) } Spacer(modifier = Modifier.width(2.dp)) Text( text = stringResource(R.string.minimum_characters, minimumCharacterCount), - color = characterCountColor, + color = BitwardenTheme.colorScheme.text.secondary, style = BitwardenTheme.typography.labelSmall, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreen.kt index 1b61b6b42..a3b47439c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/createaccount/CreateAccountScreen.kt @@ -1,14 +1,11 @@ package com.x8bit.bitwarden.ui.auth.feature.createaccount import android.widget.Toast -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -17,31 +14,23 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.testTag -import androidx.compose.ui.semantics.toggleableState -import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.core.net.toUri @@ -76,7 +65,6 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.x8bit.bitwarden.ui.platform.components.text.BitwardenClickableText import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch -import com.x8bit.bitwarden.ui.platform.components.toggle.color.bitwardenSwitchColors 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 @@ -283,6 +271,9 @@ fun CreateAccountScreen( onPrivacyPolicyClick = remember(viewModel) { { viewModel.trySendAction(PrivacyPolicyClick) } }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), ) Spacer(modifier = Modifier.navigationBarsPadding()) } @@ -290,52 +281,24 @@ fun CreateAccountScreen( } @OptIn(ExperimentalLayoutApi::class) -@Suppress("LongMethod") @Composable private fun TermsAndPrivacySwitch( isChecked: Boolean, onCheckedChange: (Boolean) -> Unit, onTermsClick: () -> Unit, onPrivacyPolicyClick: () -> Unit, + modifier: Modifier = Modifier, ) { - Row( - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .semantics(mergeDescendants = true) { - testTag = "AcceptPoliciesToggle" - toggleableState = ToggleableState(isChecked) - } - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = ripple( - color = BitwardenTheme.colorScheme.background.pressed, - ), - onClick = { onCheckedChange.invoke(!isChecked) }, - ) - .padding(start = 16.dp) - .fillMaxWidth(), - ) { - Switch( - modifier = Modifier - .height(32.dp) - .width(52.dp), - checked = isChecked, - onCheckedChange = null, - colors = bitwardenSwitchColors(), - ) - Column(Modifier.padding(start = 16.dp, top = 4.dp, bottom = 4.dp)) { - Text( - text = stringResource(id = R.string.accept_policies), - style = BitwardenTheme.typography.bodyLarge, - color = BitwardenTheme.colorScheme.text.primary, - ) + BitwardenSwitch( + modifier = modifier, + label = stringResource(id = R.string.accept_policies), + isChecked = isChecked, + contentDescription = "AcceptPoliciesToggle", + onCheckedChange = onCheckedChange, + subContent = { FlowRow( horizontalArrangement = Arrangement.Start, - modifier = Modifier - .padding(end = 16.dp) - .fillMaxWidth() - .wrapContentHeight(), + modifier = Modifier.fillMaxWidth(), ) { BitwardenClickableText( label = stringResource(id = R.string.terms_of_service), @@ -357,6 +320,6 @@ private fun TermsAndPrivacySwitch( innerPadding = PaddingValues(vertical = 4.dp, horizontal = 0.dp), ) } - } - } + }, + ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreen.kt index 0007bcd42..8d6515755 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreen.kt @@ -117,7 +117,7 @@ fun EnterpriseSignOnScreen( }, actions = { BitwardenTextButton( - label = stringResource(id = R.string.log_in), + label = stringResource(id = R.string.log_in_verb), onClick = remember(viewModel) { { viewModel.trySendAction(EnterpriseSignOnAction.LogInClick) } }, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt index f7c7dc443..d1c872ada 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginScreen.kt @@ -272,7 +272,7 @@ private fun LoginScreenContent( if (state.shouldShowLoginWithDevice) { BitwardenOutlinedButton( label = stringResource(id = R.string.log_in_with_device), - icon = rememberVectorPainter(id = R.drawable.ic_mobile), + icon = rememberVectorPainter(id = R.drawable.ic_mobile_small), onClick = onLoginWithDeviceClick, modifier = Modifier .testTag("LogInWithAnotherDeviceButton") @@ -285,7 +285,7 @@ private fun LoginScreenContent( BitwardenOutlinedButton( label = stringResource(id = R.string.log_in_sso), - icon = rememberVectorPainter(id = R.drawable.ic_briefcase), + icon = rememberVectorPainter(id = R.drawable.ic_enterprise_small), onClick = onSingleSignOnClick, modifier = Modifier .testTag("LogInWithSsoButton") diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreen.kt index 4d1a1f45f..57c3d271b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/startregistration/StartRegistrationScreen.kt @@ -2,8 +2,6 @@ package com.x8bit.bitwarden.ui.auth.feature.startregistration import android.widget.Toast import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -16,15 +14,12 @@ import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState -import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -38,8 +33,6 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction import androidx.compose.ui.semantics.customActions import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTag -import androidx.compose.ui.semantics.toggleableState -import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign @@ -70,7 +63,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.LoadingDialogState import com.x8bit.bitwarden.ui.platform.components.dropdown.EnvironmentSelector import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold -import com.x8bit.bitwarden.ui.platform.components.toggle.color.bitwardenSwitchColors +import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch 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 @@ -277,7 +270,9 @@ private fun StartRegistrationContent( isChecked = isReceiveMarketingEmailsToggled, onCheckedChange = handler.onReceiveMarketingEmailsToggle, onUnsubscribeClick = handler.onUnsubscribeMarketingEmailsClick, - modifier = Modifier.standardHorizontalMargin(), + modifier = Modifier + .fillMaxWidth() + .standardHorizontalMargin(), ) Spacer(modifier = Modifier.height(24.dp)) } @@ -361,7 +356,6 @@ private fun TermsAndPrivacyText( } } -@Suppress("LongMethod") @Composable private fun ReceiveMarketingEmailsSwitch( isChecked: Boolean, @@ -373,7 +367,9 @@ private fun ReceiveMarketingEmailsSwitch( @Suppress("MaxLineLength") val annotatedLinkString = createClickableAnnotatedString( - mainString = stringResource(id = R.string.get_emails_from_bitwarden_for_announcements_advices_and_research_opportunities_unsubscribe_any_time), + mainString = stringResource( + id = R.string.get_emails_from_bitwarden_for_announcements_advices_and_research_opportunities_unsubscribe_any_time, + ), highlights = listOf( ClickableTextHighlight( textToHighlight = unsubscribeString, @@ -381,13 +377,9 @@ private fun ReceiveMarketingEmailsSwitch( ), ), ) - Row( - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, + BitwardenSwitch( modifier = modifier .semantics(mergeDescendants = true) { - testTag = "ReceiveMarketingEmailsToggle" - toggleableState = ToggleableState(isChecked) customActions = listOf( CustomAccessibilityAction( label = unsubscribeString, @@ -397,30 +389,12 @@ private fun ReceiveMarketingEmailsSwitch( }, ), ) - } - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = ripple( - color = BitwardenTheme.colorScheme.background.pressed, - ), - onClick = { onCheckedChange.invoke(!isChecked) }, - ) - .fillMaxWidth(), - ) { - Switch( - modifier = Modifier - .height(32.dp) - .width(52.dp), - checked = isChecked, - onCheckedChange = null, - colors = bitwardenSwitchColors(), - ) - Spacer(modifier = Modifier.width(16.dp)) - Text( - text = annotatedLinkString, - style = BitwardenTheme.typography.bodyMedium, - ) - } + }, + label = annotatedLinkString, + isChecked = isChecked, + onCheckedChange = onCheckedChange, + contentDescription = "ReceiveMarketingEmailsToggle", + ) } @PreviewScreenSizes diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreen.kt index cff2e6d6c..e0b8d8ee1 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreen.kt @@ -44,7 +44,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect 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.button.BitwardenTextButton +import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme @@ -164,8 +164,8 @@ private fun WelcomeScreenContent( .fillMaxWidth(), ) - BitwardenTextButton( - label = stringResource(id = R.string.log_in), + BitwardenOutlinedButton( + label = stringResource(id = R.string.log_in_verb), onClick = onLoginClick, modifier = Modifier .standardHorizontalMargin(landscape = LANDSCAPE_HORIZONTAL_MARGIN) @@ -189,7 +189,7 @@ private fun WelcomeCardLandscape( Image( painter = rememberVectorPainter(id = state.imageRes), contentDescription = null, - modifier = Modifier.size(132.dp), + modifier = Modifier.size(124.dp), ) Column( @@ -227,7 +227,7 @@ private fun WelcomeCardPortrait( Image( painter = rememberVectorPainter(id = state.imageRes), contentDescription = null, - modifier = Modifier.size(200.dp), + modifier = Modifier.size(124.dp), ) Text( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeViewModel.kt index 53a16e3ee..8cde72b14 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeViewModel.kt @@ -82,7 +82,7 @@ data class WelcomeState( @Parcelize data object CardOne : WelcomeCard() { override val imageRes: Int get() = R.drawable.img_vault_items - override val titleRes: Int get() = R.string.privacy_prioritized + override val titleRes: Int get() = R.string.security_prioritized override val messageRes: Int get() = R.string.welcome_message_1 } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/button/BitwardenFilledIconButton.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/button/BitwardenFilledIconButton.kt new file mode 100644 index 000000000..1f8564e9d --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/button/BitwardenFilledIconButton.kt @@ -0,0 +1,55 @@ +package com.x8bit.bitwarden.ui.platform.components.button + +import androidx.annotation.DrawableRes +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.tooling.preview.Preview +import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.ui.platform.components.button.color.bitwardenFilledIconButtonColors +import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter +import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme + +/** + * A filled icon button that displays an icon. + * + * @param vectorIconRes Icon to display on the button. + * @param contentDescription The content description for this icon button. + * @param onClick Callback for when the icon button is clicked. + * @param modifier A [Modifier] for the composable. + * @param isEnabled Whether or not the button should be enabled. + */ +@Composable +fun BitwardenFilledIconButton( + @DrawableRes vectorIconRes: Int, + contentDescription: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + isEnabled: Boolean = true, +) { + FilledIconButton( + modifier = modifier.semantics(mergeDescendants = true) {}, + onClick = onClick, + colors = bitwardenFilledIconButtonColors(), + enabled = isEnabled, + ) { + Icon( + painter = rememberVectorPainter(id = vectorIconRes), + contentDescription = contentDescription, + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun BitwardenFilledIconButton_preview() { + BitwardenTheme { + BitwardenFilledIconButton( + vectorIconRes = R.drawable.ic_question_circle, + contentDescription = "Sample Icon", + onClick = {}, + ) + } +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/button/color/BitwardenIconButtonColors.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/button/color/BitwardenIconButtonColors.kt index b27d01800..b0178c6f2 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/button/color/BitwardenIconButtonColors.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/button/color/BitwardenIconButtonColors.kt @@ -5,6 +5,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme +/** + * Provides a default set of Bitwarden-styled colors for a filled icon button. + */ +@Composable +fun bitwardenFilledIconButtonColors(): IconButtonColors = IconButtonColors( + containerColor = BitwardenTheme.colorScheme.filledButton.background, + contentColor = BitwardenTheme.colorScheme.filledButton.foreground, + disabledContainerColor = BitwardenTheme.colorScheme.filledButton.backgroundDisabled, + disabledContentColor = BitwardenTheme.colorScheme.filledButton.foregroundDisabled, +) + /** * Provides a default set of Bitwarden-styled colors for a standard icon button. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/card/BitwardenContentCard.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/card/BitwardenContentCard.kt index 7b94d5cd8..423b2467c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/card/BitwardenContentCard.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/card/BitwardenContentCard.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider @@ -35,10 +36,8 @@ fun BitwardenContentCard( Column( modifier = modifier .fillMaxWidth() - .background( - color = BitwardenTheme.colorScheme.background.secondary, - shape = BitwardenTheme.shapes.content, - ), + .clip(shape = BitwardenTheme.shapes.content) + .background(color = BitwardenTheme.colorScheme.background.secondary), ) { contentItems.forEachIndexed { index, item -> Box( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/content/BitwardenContentBlock.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/content/BitwardenContentBlock.kt index c5a3d937d..c4f75a733 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/content/BitwardenContentBlock.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/content/BitwardenContentBlock.kt @@ -95,7 +95,7 @@ private fun BitwardenContentBlock( } Spacer(Modifier.height(12.dp)) } - Spacer(Modifier.width(16.dp)) + Spacer(Modifier.width(12.dp)) } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/stepper/BitwardenStepper.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/stepper/BitwardenStepper.kt index 58681d6f7..289cf8a09 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/stepper/BitwardenStepper.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/stepper/BitwardenStepper.kt @@ -7,7 +7,7 @@ import androidx.compose.ui.text.input.KeyboardType import com.x8bit.bitwarden.R import com.x8bit.bitwarden.ui.platform.base.util.ZERO_WIDTH_CHARACTER import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank -import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton +import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledIconButton import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions /** @@ -51,7 +51,7 @@ fun BitwardenStepper( value = clampedValue?.toString() ?: ZERO_WIDTH_CHARACTER, actionsTestTag = stepperActionsTestTag, actions = { - BitwardenTonalIconButton( + BitwardenFilledIconButton( vectorIconRes = R.drawable.ic_minus, contentDescription = "\u2212", onClick = { @@ -63,7 +63,7 @@ fun BitwardenStepper( isEnabled = isDecrementEnabled && !isAtRangeMinimum, modifier = Modifier.testTag("DecrementValue"), ) - BitwardenTonalIconButton( + BitwardenFilledIconButton( vectorIconRes = R.drawable.ic_plus, contentDescription = "+", onClick = { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/toggle/BitwardenSwitch.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/toggle/BitwardenSwitch.kt index a00cdbe6f..f82ee2c32 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/toggle/BitwardenSwitch.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/toggle/BitwardenSwitch.kt @@ -22,15 +22,17 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.toggleableState import androidx.compose.ui.state.ToggleableState +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.ui.platform.base.util.toAnnotatedString import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton import com.x8bit.bitwarden.ui.platform.components.toggle.color.bitwardenSwitchColors import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme /** - * A wide custom switch composable + * A custom switch composable * * @param label The descriptive text label to be displayed adjacent to the switch. * @param isChecked The current state of the switch (either checked or unchecked). @@ -45,7 +47,6 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme * in between the [label] and the toggle. This lambda extends [RowScope], allowing flexibility in * defining the layout of the actions. */ -@Suppress("LongMethod") @Composable fun BitwardenSwitch( label: String, @@ -57,6 +58,154 @@ fun BitwardenSwitch( readOnly: Boolean = false, enabled: Boolean = true, actions: (@Composable RowScope.() -> Unit)? = null, +) { + BitwardenSwitch( + modifier = modifier, + label = label.toAnnotatedString(), + isChecked = isChecked, + onCheckedChange = onCheckedChange, + contentDescription = contentDescription, + readOnly = readOnly, + enabled = enabled, + actions = actions, + subContent = { + description?.let { + Text( + text = it, + style = BitwardenTheme.typography.bodyMedium, + color = if (enabled) { + BitwardenTheme.colorScheme.text.secondary + } else { + BitwardenTheme.colorScheme.filledButton.foregroundDisabled + }, + ) + } + }, + ) +} + +/** + * A custom switch composable + * + * @param label The descriptive text label to be displayed adjacent to the switch. + * @param isChecked The current state of the switch (either checked or unchecked). + * @param onCheckedChange A lambda that is invoked when the switch's state changes. + * @param modifier A [Modifier] that you can use to apply custom modifications to the composable. + * @param description An optional description label to be displayed below the [label]. + * @param contentDescription A description of the switch's UI for accessibility purposes. + * @param readOnly Disables the click functionality without modifying the other UI characteristics. + * @param enabled Whether or not this switch is enabled. This is similar to setting [readOnly] but + * comes with some additional visual changes. + * @param actions A lambda containing the set of actions (usually icons or similar) to display + * in between the [label] and the toggle. This lambda extends [RowScope], allowing flexibility in + * defining the layout of the actions. + */ +@Composable +fun BitwardenSwitch( + label: AnnotatedString, + isChecked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + description: String? = null, + contentDescription: String? = null, + readOnly: Boolean = false, + enabled: Boolean = true, + actions: (@Composable RowScope.() -> Unit)? = null, +) { + BitwardenSwitch( + modifier = modifier, + label = label, + isChecked = isChecked, + onCheckedChange = onCheckedChange, + contentDescription = contentDescription, + readOnly = readOnly, + enabled = enabled, + actions = actions, + subContent = { + description?.let { + Text( + text = it, + style = BitwardenTheme.typography.bodyMedium, + color = if (enabled) { + BitwardenTheme.colorScheme.text.secondary + } else { + BitwardenTheme.colorScheme.filledButton.foregroundDisabled + }, + ) + } + }, + ) +} + +/** + * A custom switch composable + * + * @param label The descriptive text label to be displayed adjacent to the switch. + * @param isChecked The current state of the switch (either checked or unchecked). + * @param onCheckedChange A lambda that is invoked when the switch's state changes. + * @param modifier A [Modifier] that you can use to apply custom modifications to the composable. + * @param contentDescription A description of the switch's UI for accessibility purposes. + * @param readOnly Disables the click functionality without modifying the other UI characteristics. + * @param enabled Whether or not this switch is enabled. This is similar to setting [readOnly] but + * comes with some additional visual changes. + * @param actions A lambda containing the set of actions (usually icons or similar) to display + * in between the [label] and the toggle. This lambda extends [RowScope], allowing flexibility in + * defining the layout of the actions. + * @param subContent A lambda containing content directly below the label. + */ +@Composable +fun BitwardenSwitch( + label: String, + isChecked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + contentDescription: String? = null, + readOnly: Boolean = false, + enabled: Boolean = true, + actions: (@Composable RowScope.() -> Unit)? = null, + subContent: @Composable () -> Unit, +) { + BitwardenSwitch( + modifier = modifier, + label = label.toAnnotatedString(), + isChecked = isChecked, + onCheckedChange = onCheckedChange, + contentDescription = contentDescription, + readOnly = readOnly, + enabled = enabled, + actions = actions, + subContent = subContent, + ) +} + +/** + * A custom switch composable + * + * @param label The descriptive text label to be displayed adjacent to the switch. + * @param isChecked The current state of the switch (either checked or unchecked). + * @param onCheckedChange A lambda that is invoked when the switch's state changes. + * @param modifier A [Modifier] that you can use to apply custom modifications to the composable. + * @param contentDescription A description of the switch's UI for accessibility purposes. + * @param readOnly Disables the click functionality without modifying the other UI characteristics. + * @param enabled Whether or not this switch is enabled. This is similar to setting [readOnly] but + * comes with some additional visual changes. + * @param actions A lambda containing the set of actions (usually icons or similar) to display + * in between the [label] and the toggle. This lambda extends [RowScope], allowing flexibility in + * defining the layout of the actions. + * @param subContent A lambda containing content directly below the label. + */ +@Suppress("LongMethod") +@Composable +fun BitwardenSwitch( + label: AnnotatedString, + isChecked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + contentDescription: String? = null, + readOnly: Boolean = false, + enabled: Boolean = true, + actions: (@Composable RowScope.() -> Unit)? = null, + subContent: @Composable () -> Unit, ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -95,17 +244,7 @@ fun BitwardenSwitch( }, modifier = Modifier.testTag(tag = "SwitchText"), ) - description?.let { - Text( - text = it, - style = BitwardenTheme.typography.bodyMedium, - color = if (enabled) { - BitwardenTheme.colorScheme.text.secondary - } else { - BitwardenTheme.colorScheme.filledButton.foregroundDisabled - }, - ) - } + subContent() } actions @@ -131,6 +270,7 @@ private fun BitwardenSwitch_preview() { Column { BitwardenSwitch( label = "Label", + description = "description", isChecked = true, onCheckedChange = {}, ) @@ -141,6 +281,7 @@ private fun BitwardenSwitch_preview() { ) BitwardenSwitch( label = "Label", + description = "description", isChecked = true, onCheckedChange = {}, actions = { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreen.kt index 29e7d8d93..e20a156c1 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/debugmenu/DebugMenuScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api @@ -110,6 +111,7 @@ fun DebugMenuScreen( } }, ) + Spacer(modifier = Modifier.navigationBarsPadding()) } } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreen.kt index ed69a0603..9750c2460 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/ImportLoginsScreen.kt @@ -524,6 +524,8 @@ private fun ImportLoginsSuccessBottomSheetContent( iconVectorResource = R.drawable.ic_shield, ), ), + bottomDividerPaddingStart = 48.dp, + showBottomDivider = true, modifier = Modifier.standardHorizontalMargin(), ) { contentData -> BitwardenContentBlock( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/components/ImportLoginsInstructionStep.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/components/ImportLoginsInstructionStep.kt index ac60bd3e3..f49d9517b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/components/ImportLoginsInstructionStep.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/importlogins/components/ImportLoginsInstructionStep.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text @@ -68,8 +67,6 @@ fun ImportLoginsInstructionStep( ) { instructionStep -> InstructionRowItem( instructionStep = instructionStep, - modifier = modifier - .padding(all = 12.dp), ) } Spacer(Modifier.height(24.dp)) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemCardContent.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemCardContent.kt index 29d25f365..fd2a182d7 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemCardContent.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemCardContent.kt @@ -16,6 +16,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordFieldWithActions import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField +import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCardItemTypeHandlers import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers @@ -173,14 +174,22 @@ fun VaultItemCardContent( .padding(horizontal = 16.dp), ) Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + BitwardenTextFieldWithActions( label = stringResource(id = R.string.notes), value = notes, onValueChange = { }, readOnly = true, singleLine = false, + actions = { + BitwardenTonalIconButton( + vectorIconRes = R.drawable.ic_copy, + contentDescription = stringResource(id = R.string.copy_notes), + onClick = vaultCommonItemTypeHandlers.onCopyNotesClick, + modifier = Modifier.testTag(tag = "CipherNotesCopyButton"), + ) + }, + textFieldTestTag = "CipherNotesLabel", modifier = Modifier - .testTag("CipherNotesLabel") .fillMaxWidth() .padding(horizontal = 16.dp), ) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemIdentityContent.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemIdentityContent.kt index 6606a4bae..3e82215ff 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemIdentityContent.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemIdentityContent.kt @@ -13,19 +13,23 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField +import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers +import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultIdentityItemTypeHandlers /** * The top level content UI state for the [VaultItemScreen] when viewing a Identity cipher. */ -@Suppress("LongMethod") +@Suppress("LongMethod", "MaxLineLength") @Composable fun VaultItemIdentityContent( identityState: VaultItemState.ViewState.Content.ItemType.Identity, commonState: VaultItemState.ViewState.Content.Common, vaultCommonItemTypeHandlers: VaultCommonItemTypeHandlers, + vaultIdentityItemTypeHandlers: VaultIdentityItemTypeHandlers, modifier: Modifier = Modifier, ) { LazyColumn(modifier = modifier) { @@ -54,14 +58,14 @@ fun VaultItemIdentityContent( identityState.identityName?.let { identityName -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.identity_name), value = identityName, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_identity_name), + textFieldTestTag = "IdentityNameEntry", + copyActionTestTag = "IdentityCopyNameButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyIdentityNameClick, modifier = Modifier - .testTag("IdentityNameEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -70,14 +74,14 @@ fun VaultItemIdentityContent( identityState.username?.let { username -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.username), value = username, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_username), + textFieldTestTag = "IdentityUsernameEntry", + copyActionTestTag = "IdentityCopyUsernameButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyUsernameClick, modifier = Modifier - .testTag("IdentityUsernameEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -86,14 +90,14 @@ fun VaultItemIdentityContent( identityState.company?.let { company -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.company), value = company, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_company), + textFieldTestTag = "IdentityCompanyEntry", + copyActionTestTag = "IdentityCopyCompanyButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyCompanyClick, modifier = Modifier - .testTag("IdentityCompanyEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -102,14 +106,14 @@ fun VaultItemIdentityContent( identityState.ssn?.let { ssn -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.ssn), value = ssn, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_ssn), + textFieldTestTag = "IdentitySsnEntry", + copyActionTestTag = "IdentityCopySsnButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopySsnClick, modifier = Modifier - .testTag("IdentitySsnEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -118,14 +122,14 @@ fun VaultItemIdentityContent( identityState.passportNumber?.let { passportNumber -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.passport_number), value = passportNumber, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_passport_number), + textFieldTestTag = "IdentityPassportNumberEntry", + copyActionTestTag = "IdentityCopyPassportNumberButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyPassportNumberClick, modifier = Modifier - .testTag("IdentityPassportNumberEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -134,14 +138,14 @@ fun VaultItemIdentityContent( identityState.licenseNumber?.let { licenseNumber -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.license_number), value = licenseNumber, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_license_number), + textFieldTestTag = "IdentityLicenseNumberEntry", + copyActionTestTag = "IdentityCopyLicenseNumberButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyLicenseNumberClick, modifier = Modifier - .testTag("IdentityLicenseNumberEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -150,14 +154,14 @@ fun VaultItemIdentityContent( identityState.email?.let { email -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.email), value = email, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_email), + textFieldTestTag = "IdentityEmailEntry", + copyActionTestTag = "IdentityCopyEmailButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyEmailClick, modifier = Modifier - .testTag("IdentityEmailEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -166,14 +170,14 @@ fun VaultItemIdentityContent( identityState.phone?.let { phone -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.phone), value = phone, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_phone), + textFieldTestTag = "IdentityPhoneEntry", + copyActionTestTag = "IdentityCopyPhoneButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyPhoneClick, modifier = Modifier - .testTag("IdentityPhoneEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -182,20 +186,19 @@ fun VaultItemIdentityContent( identityState.address?.let { address -> item { Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.address), value = address, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_address), + textFieldTestTag = "IdentityAddressEntry", + copyActionTestTag = "IdentityCopyAddressButton", + onCopyClick = vaultIdentityItemTypeHandlers.onCopyAddressClick, modifier = Modifier - .testTag("IdentityAddressEntry") .fillMaxWidth() .padding(horizontal = 16.dp), ) } } - commonState.notes?.let { notes -> item { Spacer(modifier = Modifier.height(4.dp)) @@ -206,14 +209,14 @@ fun VaultItemIdentityContent( .padding(horizontal = 16.dp), ) Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + IdentityCopyField( label = stringResource(id = R.string.notes), value = notes, - onValueChange = { }, - readOnly = true, - singleLine = false, + copyContentDescription = stringResource(id = R.string.copy_notes), + textFieldTestTag = "CipherNotesLabel", + copyActionTestTag = "CipherNotesCopyButton", + onCopyClick = vaultCommonItemTypeHandlers.onCopyNotesClick, modifier = Modifier - .testTag("CipherNotesLabel") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -284,3 +287,32 @@ fun VaultItemIdentityContent( } } } + +@Composable +private fun IdentityCopyField( + label: String, + value: String, + copyContentDescription: String, + textFieldTestTag: String, + copyActionTestTag: String, + onCopyClick: () -> Unit, + modifier: Modifier = Modifier, +) { + BitwardenTextFieldWithActions( + label = label, + value = value, + onValueChange = { }, + readOnly = true, + singleLine = false, + actions = { + BitwardenTonalIconButton( + vectorIconRes = R.drawable.ic_copy, + contentDescription = copyContentDescription, + onClick = onCopyClick, + modifier = Modifier.testTag(tag = copyActionTestTag), + ) + }, + modifier = modifier, + textFieldTestTag = textFieldTestTag, + ) +} diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt index 308741fb3..fce007507 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt @@ -158,8 +158,8 @@ fun VaultItemLoginContent( Spacer(modifier = Modifier.height(8.dp)) NotesField( notes = notes, + onCopyAction = vaultCommonItemTypeHandlers.onCopyNotesClick, modifier = Modifier - .testTag("CipherNotesLabel") .fillMaxWidth() .padding(horizontal = 16.dp), ) @@ -273,14 +273,24 @@ private fun Fido2CredentialField( @Composable private fun NotesField( notes: String, + onCopyAction: () -> Unit, modifier: Modifier = Modifier, ) { - BitwardenTextField( + BitwardenTextFieldWithActions( label = stringResource(id = R.string.notes), value = notes, onValueChange = { }, readOnly = true, singleLine = false, + actions = { + BitwardenTonalIconButton( + vectorIconRes = R.drawable.ic_copy, + contentDescription = stringResource(id = R.string.copy_notes), + onClick = onCopyAction, + modifier = Modifier.testTag(tag = "CipherNotesCopyButton"), + ) + }, + textFieldTestTag = "CipherNotesLabel", modifier = modifier, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreen.kt index 5f8ca044e..268ac415a 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreen.kt @@ -44,6 +44,7 @@ import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCardItemTypeHandlers import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers +import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultIdentityItemTypeHandlers import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultLoginItemTypeHandlers import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultSshKeyItemTypeHandlers @@ -272,6 +273,9 @@ fun VaultItemScreen( vaultSshKeyItemTypeHandlers = remember(viewModel) { VaultSshKeyItemTypeHandlers.create(viewModel = viewModel) }, + vaultIdentityItemTypeHandlers = remember(viewModel) { + VaultIdentityItemTypeHandlers.create(viewModel = viewModel) + }, ) } } @@ -350,6 +354,7 @@ private fun VaultItemContent( vaultLoginItemTypeHandlers: VaultLoginItemTypeHandlers, vaultCardItemTypeHandlers: VaultCardItemTypeHandlers, vaultSshKeyItemTypeHandlers: VaultSshKeyItemTypeHandlers, + vaultIdentityItemTypeHandlers: VaultIdentityItemTypeHandlers, modifier: Modifier = Modifier, ) { when (viewState) { @@ -386,6 +391,7 @@ private fun VaultItemContent( commonState = viewState.common, identityState = viewState.type, vaultCommonItemTypeHandlers = vaultCommonItemTypeHandlers, + vaultIdentityItemTypeHandlers = vaultIdentityItemTypeHandlers, modifier = modifier, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemSecureNoteContent.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemSecureNoteContent.kt index 7fbbdfc64..9d68e3dc5 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemSecureNoteContent.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemSecureNoteContent.kt @@ -16,7 +16,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField +import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers @@ -66,14 +68,22 @@ fun VaultItemSecureNoteContent( .padding(horizontal = 16.dp), ) Spacer(modifier = Modifier.height(8.dp)) - BitwardenTextField( + BitwardenTextFieldWithActions( label = stringResource(id = R.string.notes), value = notes, onValueChange = { }, readOnly = true, singleLine = false, + actions = { + BitwardenTonalIconButton( + vectorIconRes = R.drawable.ic_copy, + contentDescription = stringResource(id = R.string.copy_notes), + onClick = vaultCommonItemTypeHandlers.onCopyNotesClick, + modifier = Modifier.testTag(tag = "CipherNotesCopyButton"), + ) + }, + textFieldTestTag = "CipherNotesLabel", modifier = Modifier - .testTag("CipherNotesLabel") .fillMaxWidth() .padding(horizontal = 16.dp), ) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt index f119ce545..021d88624 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt @@ -133,6 +133,7 @@ class VaultItemViewModel @Inject constructor( is VaultItemAction.ItemType.Login -> handleLoginTypeActions(action) is VaultItemAction.ItemType.Card -> handleCardTypeActions(action) is VaultItemAction.ItemType.SshKey -> handleSshKeyTypeActions(action) + is VaultItemAction.ItemType.Identity -> handleIdentityTypeActions(action) is VaultItemAction.Common -> handleCommonActions(action) is VaultItemAction.Internal -> handleInternalAction(action) } @@ -184,6 +185,7 @@ class VaultItemViewModel @Inject constructor( } is VaultItemAction.Common.RestoreVaultItemClick -> handleRestoreItemClicked() + is VaultItemAction.Common.CopyNotesClick -> handleCopyNotesClick() } } @@ -508,6 +510,13 @@ class VaultItemViewModel @Inject constructor( } } + private fun handleCopyNotesClick() { + onContent { content -> + val notes = content.common.notes.orEmpty() + clipboardManager.setText(text = notes) + } + } + //endregion Common Handlers //region Login Type Handlers @@ -812,6 +821,99 @@ class VaultItemViewModel @Inject constructor( //endregion SSH Key Type Handlers + //region Identity Type Handlers + + private fun handleIdentityTypeActions(action: VaultItemAction.ItemType.Identity) { + when (action) { + VaultItemAction.ItemType.Identity.CopyIdentityNameClick -> { + handleCopyIdentityNameClick() + } + + VaultItemAction.ItemType.Identity.CopyUsernameClick -> { + handleCopyIdentityUsernameClick() + } + + VaultItemAction.ItemType.Identity.CopyCompanyClick -> handleCopyCompanyClick() + VaultItemAction.ItemType.Identity.CopySsnClick -> handleCopySsnClick() + VaultItemAction.ItemType.Identity.CopyPassportNumberClick -> { + handleCopyPassportNumberClick() + } + + VaultItemAction.ItemType.Identity.CopyLicenseNumberClick -> { + handleCopyLicenseNumberClick() + } + + VaultItemAction.ItemType.Identity.CopyEmailClick -> handleCopyEmailClick() + VaultItemAction.ItemType.Identity.CopyPhoneClick -> handleCopyPhoneClick() + VaultItemAction.ItemType.Identity.CopyAddressClick -> handleCopyAddressClick() + } + } + + private fun handleCopyIdentityNameClick() { + onIdentityContent { _, identity -> + val identityName = identity.identityName.orEmpty() + clipboardManager.setText(text = identityName) + } + } + + private fun handleCopyIdentityUsernameClick() { + onIdentityContent { _, identity -> + val username = identity.username.orEmpty() + clipboardManager.setText(text = username) + } + } + + private fun handleCopyCompanyClick() { + onIdentityContent { _, identity -> + val company = identity.company.orEmpty() + clipboardManager.setText(text = company) + } + } + + private fun handleCopySsnClick() { + onIdentityContent { _, identity -> + val ssn = identity.ssn.orEmpty() + clipboardManager.setText(text = ssn) + } + } + + private fun handleCopyPassportNumberClick() { + onIdentityContent { _, identity -> + val passportNumber = identity.passportNumber.orEmpty() + clipboardManager.setText(text = passportNumber) + } + } + + private fun handleCopyLicenseNumberClick() { + onIdentityContent { _, identity -> + val licenseNumber = identity.licenseNumber.orEmpty() + clipboardManager.setText(text = licenseNumber) + } + } + + private fun handleCopyEmailClick() { + onIdentityContent { _, identity -> + val email = identity.email.orEmpty() + clipboardManager.setText(text = email) + } + } + + private fun handleCopyPhoneClick() { + onIdentityContent { _, identity -> + val phone = identity.phone.orEmpty() + clipboardManager.setText(text = phone) + } + } + + private fun handleCopyAddressClick() { + onIdentityContent { _, identity -> + val address = identity.address.orEmpty() + clipboardManager.setText(text = address) + } + } + + //endregion Identity Type Handlers + //region Internal Type Handlers private fun handleInternalAction(action: VaultItemAction.Internal) { @@ -1133,6 +1235,21 @@ class VaultItemViewModel @Inject constructor( } } } + + private inline fun onIdentityContent( + crossinline block: ( + VaultItemState.ViewState.Content, + VaultItemState.ViewState.Content.ItemType.Identity, + ) -> Unit, + ) { + state.viewState.asContentOrNull() + ?.let { content -> + (content.type as? VaultItemState.ViewState.Content.ItemType.Identity) + ?.let { identityContent -> + block(content, identityContent) + } + } + } } /** @@ -1724,6 +1841,11 @@ sealed class VaultItemAction { * The user confirmed cloning a cipher without its FIDO 2 credentials. */ data object ConfirmCloneWithoutFido2CredentialClick : Common() + + /** + * The user has clicked the copy button for notes text field. + */ + data object CopyNotesClick : Common() } /** @@ -1827,6 +1949,56 @@ sealed class VaultItemAction { */ data object CopyFingerprintClick : SshKey() } + + /** + * Represents actions specific to the Identity type. + */ + sealed class Identity : VaultItemAction() { + /** + * The user has clicked the copy button for the identity name. + */ + data object CopyIdentityNameClick : Identity() + + /** + * The user has clicked the copy button for the username. + */ + data object CopyUsernameClick : Identity() + + /** + * The user has clicked the copy button for the company. + */ + data object CopyCompanyClick : Identity() + + /** + * The user has clicked the copy button for the SSN. + */ + data object CopySsnClick : Identity() + + /** + * The user has clicked the copy button for the passport number. + */ + data object CopyPassportNumberClick : Identity() + + /** + * The user has clicked the copy button for the license number. + */ + data object CopyLicenseNumberClick : Identity() + + /** + * The user has clicked the copy button for the email. + */ + data object CopyEmailClick : Identity() + + /** + * The user has clicked the copy button for the phone number. + */ + data object CopyPhoneClick : Identity() + + /** + * The user has clicked the copy button for the address. + */ + data object CopyAddressClick : Identity() + } } /** diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultCommonItemTypeHandlers.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultCommonItemTypeHandlers.kt index ec011346e..7dfa682ca 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultCommonItemTypeHandlers.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultCommonItemTypeHandlers.kt @@ -21,6 +21,7 @@ data class VaultCommonItemTypeHandlers( Boolean, ) -> Unit, val onAttachmentDownloadClick: (VaultItemState.ViewState.Content.Common.AttachmentItem) -> Unit, + val onCopyNotesClick: () -> Unit, ) { @Suppress("UndocumentedPublicClass") companion object { @@ -52,6 +53,9 @@ data class VaultCommonItemTypeHandlers( onAttachmentDownloadClick = { viewModel.trySendAction(VaultItemAction.Common.AttachmentDownloadClick(it)) }, + onCopyNotesClick = { + viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick) + }, ) } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultIdentityItemTypeHandlers.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultIdentityItemTypeHandlers.kt new file mode 100644 index 000000000..d44ac6066 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultIdentityItemTypeHandlers.kt @@ -0,0 +1,59 @@ +package com.x8bit.bitwarden.ui.vault.feature.item.handlers + +import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemAction +import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemViewModel + +/** + * A collection of handler functions for managing actions within the context of viewing identity + * items in a vault. + */ +data class VaultIdentityItemTypeHandlers( + val onCopyIdentityNameClick: () -> Unit, + val onCopyUsernameClick: () -> Unit, + val onCopyCompanyClick: () -> Unit, + val onCopySsnClick: () -> Unit, + val onCopyPassportNumberClick: () -> Unit, + val onCopyLicenseNumberClick: () -> Unit, + val onCopyEmailClick: () -> Unit, + val onCopyPhoneClick: () -> Unit, + val onCopyAddressClick: () -> Unit, +) { + @Suppress("UndocumentedPublicClass", "MaxLineLength") + companion object { + /** + * Creates the [VaultIdentityItemTypeHandlers] using the [viewModel] to send desired actions. + */ + fun create( + viewModel: VaultItemViewModel, + ): VaultIdentityItemTypeHandlers = + VaultIdentityItemTypeHandlers( + onCopyIdentityNameClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyIdentityNameClick) + }, + onCopyUsernameClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyUsernameClick) + }, + onCopyCompanyClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyCompanyClick) + }, + onCopySsnClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopySsnClick) + }, + onCopyPassportNumberClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPassportNumberClick) + }, + onCopyLicenseNumberClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyLicenseNumberClick) + }, + onCopyEmailClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyEmailClick) + }, + onCopyPhoneClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPhoneClick) + }, + onCopyAddressClick = { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyAddressClick) + }, + ) + } +} diff --git a/app/src/main/res/drawable/ic_briefcase.xml b/app/src/main/res/drawable/ic_briefcase.xml deleted file mode 100644 index 48c500a25..000000000 --- a/app/src/main/res/drawable/ic_briefcase.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_enterprise_small.xml b/app/src/main/res/drawable/ic_enterprise_small.xml new file mode 100644 index 000000000..ea40d6e8b --- /dev/null +++ b/app/src/main/res/drawable/ic_enterprise_small.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_mobile.xml b/app/src/main/res/drawable/ic_mobile.xml deleted file mode 100644 index 1f26b04b5..000000000 --- a/app/src/main/res/drawable/ic_mobile.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_mobile_small.xml b/app/src/main/res/drawable/ic_mobile_small.xml new file mode 100644 index 000000000..9523892dc --- /dev/null +++ b/app/src/main/res/drawable/ic_mobile_small.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_number1.xml b/app/src/main/res/drawable/ic_number1.xml index 70c814975..07586068c 100644 --- a/app/src/main/res/drawable/ic_number1.xml +++ b/app/src/main/res/drawable/ic_number1.xml @@ -1,13 +1,13 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M0,0.333h24v24h-24z"/> diff --git a/app/src/main/res/drawable/ic_number2.xml b/app/src/main/res/drawable/ic_number2.xml index 3937b2d4c..8c42b86c0 100644 --- a/app/src/main/res/drawable/ic_number2.xml +++ b/app/src/main/res/drawable/ic_number2.xml @@ -1,17 +1,9 @@ - - - - - - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_number3.xml b/app/src/main/res/drawable/ic_number3.xml index 65d016ae6..1a882b95d 100644 --- a/app/src/main/res/drawable/ic_number3.xml +++ b/app/src/main/res/drawable/ic_number3.xml @@ -1,17 +1,13 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - - - - + android:pathData="M0,0.333h24v24h-24z"/> + diff --git a/app/src/main/res/drawable/ic_number4.xml b/app/src/main/res/drawable/ic_number4.xml index 229f2704a..9644de83e 100644 --- a/app/src/main/res/drawable/ic_number4.xml +++ b/app/src/main/res/drawable/ic_number4.xml @@ -1,17 +1,13 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - - - - + android:pathData="M0,0.333h24v24h-24z"/> + diff --git a/app/src/main/res/values-af-rZA/strings.xml b/app/src/main/res/values-af-rZA/strings.xml index 7a0f44470..48cbf0658 100644 --- a/app/src/main/res/values-af-rZA/strings.xml +++ b/app/src/main/res/values-af-rZA/strings.xml @@ -952,7 +952,7 @@ Wil u na die rekening omskakel? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml index 541ed98f7..11b653501 100644 --- a/app/src/main/res/values-ar-rSA/strings.xml +++ b/app/src/main/res/values-ar-rSA/strings.xml @@ -952,7 +952,7 @@ أو تسجيل الدخول، قد يكون لديك حساب بالفعل. الحصول على رسائل البريد الإلكتروني من Bitwarden للحصول على الإعلانات والنصائح وفرص البحث. إلغاء الاشتراك في أي وقت. احصل على المشورة والإعلانات وفرص البحث من Bitwarden في صندوق الوارد الخاص بك. قم بإلغاء الاشتراك في أي وقت. - الخصوصية، مرتبة حسب الأولوية + الخصوصية، مرتبة حسب الأولوية حفظ تسجيلات الدخول والبطاقات والهويات إلى خزانتك الآمنة. يستخدم Bitwarden المعرفة الصفرية، التشفير من النهاية إلى النهاية لحماية ما هو مهم بالنسبة لك. قم بإعداد فتح القفل الحيوي والتعبئة التلقائية لتسجيل الدخول إلى حساباتك دون كتابة حرف واحد. تسجيل دخول سريع وسهل diff --git a/app/src/main/res/values-az-rAZ/strings.xml b/app/src/main/res/values-az-rAZ/strings.xml index ce6818125..419cde4e1 100644 --- a/app/src/main/res/values-az-rAZ/strings.xml +++ b/app/src/main/res/values-az-rAZ/strings.xml @@ -951,7 +951,7 @@ Bu hesaba keçmək istəyirsiniz? Ya da bir hesabınız varsa giriş edin. Elanlar, məsləhətlər və araşdırma fürsətləri üçün Bitwarden-dən e-poçt alın. İstənilən vaxt abunəlikdən çıxa bilərsiniz. Gələn qutunuzda Bitwarden-dən məsləhət, elan və araşdırma imkanları ilə bağlı məktublar alın. İstənilən vaxt abunəlikdən çıxa bilərsiniz. - Məxfilik, prioritetdir + Məxfilik, prioritetdir Girişləri, kart və kimlik məlumatlarınızı güvənli seyfinizdə saxlayın. Bitwarden, sizin üçün mühüm olanları qorumaq üçün zero-knowledge və ucdan-uca şifrələmə istifadə edir. Tək bir hərf yazmadan hesablarınıza giriş etmək üçün biometrik kilid açma və avto-doldurma seçimlərini qurun. Cəld və asan giriş diff --git a/app/src/main/res/values-be-rBY/strings.xml b/app/src/main/res/values-be-rBY/strings.xml index 1cd9c0e60..5abc8677a 100644 --- a/app/src/main/res/values-be-rBY/strings.xml +++ b/app/src/main/res/values-be-rBY/strings.xml @@ -951,7 +951,7 @@ Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 988b80ceb..fe13e338c 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -952,7 +952,7 @@ Или се впишете – може вече да имате регистрация. Получавайте е-писма от Битоурден за новини, съвети и възможности за проучвания. Можете да се отпишете по всяко време. Получавайте съвети, обявления и предложения за участия в проучвания от Битоурден в е-пощата си. Можете да се отпишете по всяко време. - Поверителността е от първостепенна важност + Поверителността е от първостепенна важност Съхранявайте данни за вписване, карти и самоличности в своя защитен трезор. Битуорден използва шифроване от край до край за да пази това, което е важно за Вас. Настройте отключването чрез биометрични данни и автоматичното попълване, за да се вписвате в регистрациите си без да натискате и един клавиш. Бързо и лесно вписване diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index c72e68da5..a4066ae83 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-bs-rBA/strings.xml b/app/src/main/res/values-bs-rBA/strings.xml index 7a4afe753..83870b072 100644 --- a/app/src/main/res/values-bs-rBA/strings.xml +++ b/app/src/main/res/values-bs-rBA/strings.xml @@ -951,7 +951,7 @@ Skeniranje će biti izvršeno automatski. Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml index 87a17274a..c8e205848 100644 --- a/app/src/main/res/values-ca-rES/strings.xml +++ b/app/src/main/res/values-ca-rES/strings.xml @@ -952,7 +952,7 @@ Voleu canviar a aquest compte? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 60a94513c..df83e3686 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -951,7 +951,7 @@ Chcete se přepnout na tento účet? Nebo se přihlaste, možná už máte účet. Získejte e-maily od Bitwardenu pro oznámení, poradenství a výzkumné příležitosti. Můžete se kdykoli odhlásit. Dostávejte do své e-mailové schránky rady, oznámení a příležitosti k výzkumu od společnosti Bitwarden. Odhlásit odběr můžete kdykoli. - Soukromí, upřednostněno + Soukromí, upřednostněno Uložte si přihlašovací údaje, karty a identity do zabezpečeného trezoru. Bitwarden používá šifrování end-to-end s nulovou znalostí, aby ochránil to, co je pro Vás důležité. Nastavte biometrické odemknutí a automatické vyplňování pro přihlášení k Vašim účtům bez zadání jediného písmene. Rychlé a snadné přihlášení diff --git a/app/src/main/res/values-cy-rGB/strings.xml b/app/src/main/res/values-cy-rGB/strings.xml index 180d2aee8..e2eda764a 100644 --- a/app/src/main/res/values-cy-rGB/strings.xml +++ b/app/src/main/res/values-cy-rGB/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml index 00862beda..82e778a05 100644 --- a/app/src/main/res/values-da-rDK/strings.xml +++ b/app/src/main/res/values-da-rDK/strings.xml @@ -952,7 +952,7 @@ Skift til denne konto? Eller log ind, da en konto muligvis allerede eksisterer. Få e-mails fra Bitwarden vedr. bekendtgørelser, råd og forskningsmuligheder. Afmeld når som helst. Få råd, bekendtgørelser og forskningsmuligheder fra Bitwarden i indbakken. Afmeld til enhver tid. - Fortrolighed, prioriteret + Fortrolighed, prioriteret Gem logins, kort og identiteter i den sikre boks. Bitwarden bruger nul-viden, ende-til-ende kryptering til at beskytte brugerens vigtigt ting. Opsæt biometrisk oplåsning og autofyld for at logge ind på konti uden at skrive ét enkelt bogstav. Hurtig og nem indlogning diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index a1a149a3f..860d45383 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -951,7 +951,7 @@ Möchtest du zu diesem Konto wechseln? Oder melde dich an, du hast möglicherweise bereits ein Konto. Erhalte E-Mails von Bitwarden für Ankündigungen, Ratschläge und Marktforschungsumfragen. Melde dich jederzeit wieder ab. Erhalte Ratschläge, Ankündigungen und Marktforschungsumfragen von Bitwarden in deinem Posteingang. Melde dich jederzeit wieder ab. - Datenschutz, priorisiert + Datenschutz, priorisiert Speichere Zugangsdaten, Karten und Identitäten in deinem sicheren Tresor. Bitwarden verwendet Zero-Knowledge und Ende-zu-Ende-Verschlüsselung, um das zu schützen, was für dich wichtig ist. Richte biometrisches Entsperren und Auto-Ausfüllen ein, um dich ohne einen einzigen Buchstaben einzugeben bei deinen Konten anzumelden. Schnelle und einfache Anmeldung diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml index a4f2f00a5..679b44a3b 100644 --- a/app/src/main/res/values-el-rGR/strings.xml +++ b/app/src/main/res/values-el-rGR/strings.xml @@ -241,7 +241,7 @@ Κωδικός επαλήθευσης (TOTP) Το κλειδί επαλήθευσης προστέθηκε. Αδυναμία ανάγνωσης κλειδιού επαλήθευσης. - Σημαδέψτε με την κάμερα σας τον κωδικό QR. + Σημαδέψτε με την κάμερα σας τον κωδικό QR. Η σάρωση θα γίνει αυτόματα. Σάρωση κώδικα QR Κάμερα @@ -952,7 +952,7 @@ Ή συνδεθείτε, μπορεί να έχετε ήδη λογαριασμό. Λάβετε μηνύματα ηλ. ταχυδρομείου από το Bitwarden για ανακοινώσεις, συμβουλές και ερευνητικές ευκαιρίες. Ακυρώστε τη συνδρομή σας ανα πάσα στιγμή. Λάβετε συμβουλές, ανακοινώσεις και ευκαιρίες έρευνας από το Bitwarden στα εισερχόμενά σας. Μπορείτε να απεγγραφείτε ανα πάσα στιγμή. - Προταιρεότητα, η ιδιωτικότητα + Προταιρεότητα, η ιδιωτικότητα Αποθηκεύστε συνδέσεις, κάρτες και ταυτότητες στο ασφαλές θησαυ/κιό σας. Το Bitwarden χρησιμοποιεί κρυπτογράφηση από άκρο σε άκρο και μηδενικής γνώσης για να προστατεύσει ό,τι είναι σημαντικό για εσάς. Ρυθμίστε το βιομετρικό ξεκλείδωμα και την αυτόματη συμπλήρωση για σύνδεση στους λογαριασμούς σας χωρίς να πληκτρολογήσετε ούτε ένα γράμμα. Γρήγορη και εύκολη σύνδεση diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml index 8584ec0ee..7690f8b89 100644 --- a/app/src/main/res/values-en-rGB/strings.xml +++ b/app/src/main/res/values-en-rGB/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritised + Privacy, prioritised Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-en-rIN/strings.xml b/app/src/main/res/values-en-rIN/strings.xml index 5a6e81491..b1357e285 100644 --- a/app/src/main/res/values-en-rIN/strings.xml +++ b/app/src/main/res/values-en-rIN/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritised + Privacy, prioritised Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 5f0633191..8b56b38c6 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -953,7 +953,7 @@ seleccione Agregar TOTP para almacenar la clave de forma segura Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 98217a2fd..c65ab49eb 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -952,7 +952,7 @@ Soovid selle konto peale lülituda? Või logi sisse, sul võib olla konto juba olemas. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-eu-rES/strings.xml b/app/src/main/res/values-eu-rES/strings.xml index 016b8e5b7..93677fd70 100644 --- a/app/src/main/res/values-eu-rES/strings.xml +++ b/app/src/main/res/values-eu-rES/strings.xml @@ -950,7 +950,7 @@ Kontu honetara aldatu nahi duzu? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-fa-rIR/strings.xml b/app/src/main/res/values-fa-rIR/strings.xml index 6696393df..46a3996ac 100644 --- a/app/src/main/res/values-fa-rIR/strings.xml +++ b/app/src/main/res/values-fa-rIR/strings.xml @@ -952,7 +952,7 @@ Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index dff43124e..e1b456332 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -952,7 +952,7 @@ Haluatko vaihtaa tähän tiliin? Tai kirjaudu sisään. Sinulla saattaa jo olla tili. Vastaanota Bitwardenilta uutiskirjeitä julkaisuista, tukiresursseista ja tutkimusmahdollisuuksista. Lopeta tilaus milloin tahansa. Vastaanota Bitwardenilta postilaatikkoosi vinkkejä, uutisia ja tutkimusmahdollisuuksia. Lopeta tilaus milloin tahansa. - Yksityisyys etusijalla + Yksityisyys etusijalla Tallenna kirjautumistiedot, kortit and henkilöllisyydet suojattuun holviisi. Bitwarden suojaa tärkeät tietosi nollatietoisella päästä pähän -salauksella. Määritä biometrinen avaus ja automaattitäyttö kirjautuaksesi tileillesi kirjoittamatta yhtään kirjainta. Nopea ja helppo kirjautuminen diff --git a/app/src/main/res/values-fil-rPH/strings.xml b/app/src/main/res/values-fil-rPH/strings.xml index c1d8a4874..84dd1aa5a 100644 --- a/app/src/main/res/values-fil-rPH/strings.xml +++ b/app/src/main/res/values-fil-rPH/strings.xml @@ -952,7 +952,7 @@ Gusto mo bang pumunta sa account na ito? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 384fad6a2..3b2a413aa 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -952,7 +952,7 @@ Voulez-vous basculer vers ce compte ? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Connexion rapide et facile diff --git a/app/src/main/res/values-gl-rES/strings.xml b/app/src/main/res/values-gl-rES/strings.xml index b22cc6b21..125e5b857 100644 --- a/app/src/main/res/values-gl-rES/strings.xml +++ b/app/src/main/res/values-gl-rES/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index 2ecfdbd68..97c6451ef 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -951,7 +951,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-hr-rHR/strings.xml b/app/src/main/res/values-hr-rHR/strings.xml index d94afcb33..fd72ca6f3 100644 --- a/app/src/main/res/values-hr-rHR/strings.xml +++ b/app/src/main/res/values-hr-rHR/strings.xml @@ -950,7 +950,7 @@ Ili se prijavi, ako već imaš račun. Primaj e-poštu od Bitwardena s najavamam savjetima i mogućnostima istraživanja. Otkaži pretplatu u bilo kojem trenutku. Primaj savjete, najave i mogućnosti istraživanja od Bitwardena u svoju Ulaznu poštu. Otkaži pretplatu u bilo kojem trenutku. - Privatnost na prvom mjestu + Privatnost na prvom mjestu Spremi prijave, kartice i identitete u svoj sigurni trezor. Bitwarden koristi end-to-end enkripciju bez znanja kako bi zaštitio ono što ti je važno. Uključi biometrijsko otključavanje i auto-ispunu za prijavu bez utipkavanja ijednog slova. Brza i jednostavna prijava diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index f0f726f2a..ddf751866 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -691,7 +691,7 @@ Kód megadása manuálisan TOTP hozzáadása TOTP beállítása - A kulcs sikeres megadása után válasszuk ki a + A kulcs sikeres megadása után válasszuk ki a TOTP hozzáadása a kulcs biztonságos tárolásához lehetőséget. Ha a zárolási lehetőségeket \"Soha” értékre állítjuk, akkor a széf bárki számára elérhető lesz, aki hozzáfér az eszközhöz. Ha ezt az opciót használjuk, akkor gondoskodni kell arról, hogy az eszköz megfelelően védett legyen. A beírt webcímek közül egy vagy több érvénytelen. Vizsgáljuk át és próbáljuk meg újra elmenteni. @@ -951,7 +951,7 @@ Szeretnénk átváltani erre a fiókra? Vagy Bejelentkezés, ha már van fiók. Emaileket kaphatunk a Bitwardentől bejelentésekről, tanácsokról és kutatási lehetőségekről. Bármikor leiratkozhatunk. Tanácsokat, bejelentéseket és kutatási lehetőségeket kaphatunk a Bitwardentől a postaládába. Bármikor leiratkozhatunk. - Adatvédelem, prioritás + Adatvédelem, prioritás Mentsük el a bejelentkezési adatokat, kártyákat és azonosításokat a biztonságos széfbe. A Bitwarden tudás nélküli, végpontok közötti titkosítást használ a felhasználók számára fontos dolgok védelmére. Állítsuk be a biometrikus feloldást és az automatikus kitöltést, hogy egyetlen betű beírása nélkül jelentkezzünk be a fiókokba. Gyors és könnyű bejelentkezés diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index 74b9bfa9e..61321e485 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index e9204acce..82cfbae25 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -951,7 +951,7 @@ Vuoi passare a questo account? Oppure effettua l\'accesso se potresti avere già un account. Ricevi email da Bitwarden per annunci, consigli e opportunità di ricerca. Disiscriviti in qualsiasi momento. Ottieni consigli, annunci e opportunità di ricerca da Bitwarden nella tua casella di posta. Disiscriviti in qualsiasi momento. - Privacy, al primo posto + Privacy, al primo posto Salva login, carte e identità nella tua cassaforte sicura. Bitwarden usa la crittografia end-to-end e zero-knowledge per proteggere ciò che è importante. Imposta lo sblocco biometrico e il riempimento automatico per accedere ai tuoi account senza digitare una sola lettera. Accesso facile e veloce diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml index 246854ec0..a3d1a7743 100644 --- a/app/src/main/res/values-iw-rIL/strings.xml +++ b/app/src/main/res/values-iw-rIL/strings.xml @@ -207,7 +207,7 @@ כבוי פעיל סטטוס - הדרך הקלה ביותר להוספת סיסמאות לכספת היא דרך שירות ההשלמה האוטומטי של Bitwarden. למד עוד אודות השימוש ביכולת ההשלמה האוטומטית של + הדרך הקלה ביותר להוספת סיסמאות לכספת היא דרך שירות ההשלמה האוטומטי של Bitwarden. למד עוד אודות השימוש ביכולת ההשלמה האוטומטית של Bitwarden בעזרת פתיחת חלון \"הגדרות\". השלמה אוטומטית האם ברצונך להפעיל את ההשלמה האוטומטית או לצפות בפריט זה? @@ -955,7 +955,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 369c7dfd9..ff2471cbd 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -952,7 +952,7 @@ またはログインしてください。すでにアカウントを持っている可能性があります。 Bitwarden からメールでお知らせやアドバイス、アンケートを受け取ります。配信停止はいつでもできます。 Bitwarden からメールでアドバイスや告知、アンケートを受け取りましょう。配信停止はいつでもできます。 - プライバシーが最優先 + プライバシーが最優先 ログイン情報、カード、ID を安全な保管庫に保存します。Bitwarden はゼロ知識、エンドツーエンドの暗号化を使用して、あなたにとって重要なものを保護します。 生体認証のロック解除と自動入力を設定して、文字を何も入力せずにアカウントにログインします。 すばやく簡単にログイン diff --git a/app/src/main/res/values-ka-rGE/strings.xml b/app/src/main/res/values-ka-rGE/strings.xml index 4a5773e9f..cfbdae808 100644 --- a/app/src/main/res/values-ka-rGE/strings.xml +++ b/app/src/main/res/values-ka-rGE/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-kn-rIN/strings.xml b/app/src/main/res/values-kn-rIN/strings.xml index 66712e57d..9e4289f8e 100644 --- a/app/src/main/res/values-kn-rIN/strings.xml +++ b/app/src/main/res/values-kn-rIN/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 16f88b2e7..65b76391c 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -952,7 +952,7 @@ Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index b258a10c0..ff286829c 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -952,7 +952,7 @@ Ar norite pereiti prie šios paskyros? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-lv-rLV/strings.xml b/app/src/main/res/values-lv-rLV/strings.xml index 57f98d0d6..2cf06e9bf 100644 --- a/app/src/main/res/values-lv-rLV/strings.xml +++ b/app/src/main/res/values-lv-rLV/strings.xml @@ -952,7 +952,7 @@ Vai pārslēgties uz šo kontu? Vai jāmēģina pieteikties, varbūt Tev jau ir konts. Saņemt e-pasta ziņojumus no Bitwarden par paziņojumiem, padomiem un izpētes iespējām. Abonēšanu atteikt var jebkurā brīdī. Iegūt savā iesūtnē padomus, paziņojumus un izpētes iespējas no Bitwarden. Atrakstīties var jebkurā brīdī. - Privātums pirmajā vietā + Privātums pirmajā vietā Pieteikšanās vienumu, karšu un identitāšu glabāšana savā drošajā glabātavā. Bitwarden izmanto nulles zināšanu pilnīgu šifrēšanu, lai aizsargātu to, kas Tev ir svarīgs. Iestati biometrisko atslēgšanu un automātiski aizpildi, lai pieteiktos savos kontos bez neviena burta ievadīšanas. Ātra un vienkārša pieteikšanās diff --git a/app/src/main/res/values-ml-rIN/strings.xml b/app/src/main/res/values-ml-rIN/strings.xml index 6bf3aea72..9a7dfeb4f 100644 --- a/app/src/main/res/values-ml-rIN/strings.xml +++ b/app/src/main/res/values-ml-rIN/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-mr-rIN/strings.xml b/app/src/main/res/values-mr-rIN/strings.xml index 5d4bd2163..3cc83e5a4 100644 --- a/app/src/main/res/values-mr-rIN/strings.xml +++ b/app/src/main/res/values-mr-rIN/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-my-rMM/strings.xml b/app/src/main/res/values-my-rMM/strings.xml index 4a5773e9f..cfbdae808 100644 --- a/app/src/main/res/values-my-rMM/strings.xml +++ b/app/src/main/res/values-my-rMM/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index bda7895b6..7fb76203c 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -952,7 +952,7 @@ Vil du bytte til denne kontoen? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-ne-rNP/strings.xml b/app/src/main/res/values-ne-rNP/strings.xml index 4a5773e9f..cfbdae808 100644 --- a/app/src/main/res/values-ne-rNP/strings.xml +++ b/app/src/main/res/values-ne-rNP/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml index a9fdf8ae7..d0ab9ef0d 100644 --- a/app/src/main/res/values-nl-rNL/strings.xml +++ b/app/src/main/res/values-nl-rNL/strings.xml @@ -952,7 +952,7 @@ Wilt u naar dit account wisselen? Of ga naar inloggen, je hebt mogelijk al een account. Ontvang e-mailberichten van Bitwarden voor aankondigingen, advies en onderzoeksmogelijkheden. Afmelden kan op ieder moment. Ontvang advies, aankondigingen en onderzoeksmogelijkheden van Bitwarden in je inbox. Je kunt je op ieder moment uitschrijven. - Privacy, geprioriteerd + Privacy, geprioriteerd Logins, kaarten en identiteiten in je beveiligde kluis opslaan. Bitwarden gebruikt zero-knowledge, end-to-end versleuteling om te beschermen wat belangrijk voor jou is. Biometrische ontgrendelen en automatisch invullen instellen zodat je kunt inloggen op je accounts zonder één letter te typen. Snel en eenvoudig inloggen diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml index a2e8a6118..e0a07d395 100644 --- a/app/src/main/res/values-nn-rNO/strings.xml +++ b/app/src/main/res/values-nn-rNO/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Eller logg inn, det kan hende du allereie har ein konto. Få e-post frå Bitwarden for kunngjeringar, råd og forskingsmoglegheiter. Slutt å abonnere på det når som helst. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-or-rIN/strings.xml b/app/src/main/res/values-or-rIN/strings.xml index ce41f1c84..1573680da 100644 --- a/app/src/main/res/values-or-rIN/strings.xml +++ b/app/src/main/res/values-or-rIN/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 1a8940ba8..8a60f8790 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -952,7 +952,7 @@ Czy chcesz przełączyć się na to konto? Lub zaloguj się, jeśli już posiadasz konto. Otrzymuj wiadomości e-mail od Bitwarden z ogłoszeniami, poradami i możliwościami badawczymi. Możesz anulować subskrypcję w dowolnym momencie. Otrzymuj porady, ogłoszenia i możliwości badawcze od Bitwarden w swojej skrzynce odbiorczej. Możesz anulować subskrypcję w dowolnym momencie. - Privacy, prioritized + Privacy, prioritized Zapisz dane logowania, karty i tożsamości w bezpiecznym sejfie. Bitwarden używa szyfrowania end-to-end w celu ochrony tego, co jest dla Ciebie ważne. Skonfiguruj odblokowanie i autouzupełnianie biometryczne, aby zalogować się na swoje konta bez wpisywania nawet pojedynczej litery. Szybkie i łatwe logowanie diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index f8d3bea1c..9d58e26a6 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -952,7 +952,7 @@ Você deseja mudar para esta conta? Ou faça o login, você pode já ter uma conta. Obtenha e-mails do Bitwarden para anúncios, conselhos e oportunidades de pesquisa. Cancele a inscrição a qualquer momento. Receba conselhos, anúncios e oportunidades de pesquisa da Bitwarden na sua caixa de entrada. Cancele a assinatura a qualquer momento. - Privacidade, priorizado + Privacidade, priorizado Salve suas credenciais, cartões e identidades no seu cofre seguro. O Bitwarden usa criptografia de zero conhecimento, de ponta a ponta para proteger o que é importante para você. Configure o desbloqueio biométrico e preenchimento automático para acessar suas contas sem digitar uma única letra. Login rápido e fácil diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 10cef5263..e150d8dd5 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -952,7 +952,7 @@ Pretende mudar para esta conta? Receba e-mails do Bitwarden com anúncios, conselhos e oportunidades de investigação. Anule a subscrição em qualquer altura. Receba conselhos, anúncios e oportunidades de investigação do Bitwarden na sua caixa de entrada. Anule a subscrição a qualquer altura. - Privacidade, priorizada + Privacidade, priorizada Guarde credenciais, cartões e identidades no seu cofre seguro. O Bitwarden utiliza encriptação de conhecimento zero e de ponto a ponto para proteger o que é importante para si. Configure o desbloqueio biométrico e o preenchimento automático para iniciar sessão nas suas contas sem escrever uma única letra. Início de sessão rápido e fácil diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index e28e4c812..9aa7396d5 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -952,7 +952,7 @@ Doriți să comutați la acest cont? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Autentificare rapidă și ușoară diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index aecaabdc5..69e67145a 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -954,7 +954,7 @@ Или авторизуйтесь, возможно, у вас уже есть аккаунт. Получайте электронные письма от Bitwarden с анонсами, советами и возможностями для исследований. Отписаться можно в любое время. Получайте советы, объявления и исследования от Bitwarden в свой почтовый ящик. Отписаться можно в любое время. - Конфиденциальность, приоритет + Конфиденциальность, приоритет Сохраняйте логины, карты и личные данные в своем защищенном хранилище. Bitwarden использует сквозное шифрование, чтобы защитить то, что для вас важно. Настройте биометрическую разблокировку и автозаполнение, чтобы входить в свои аккаунты, не набирая ни одной буквы. Быстрая и простая авторизация diff --git a/app/src/main/res/values-si-rLK/strings.xml b/app/src/main/res/values-si-rLK/strings.xml index bf9ba5f1c..d175078c3 100644 --- a/app/src/main/res/values-si-rLK/strings.xml +++ b/app/src/main/res/values-si-rLK/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index d7161250a..3bb358fb5 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -952,7 +952,7 @@ Chcete prepnúť na tento účet? Alebo sa prihláste, možno už účet máte. Dostávať e-maily od Bitwardenu s oznámeniami, radami a možnosťami výskumu. Odhlásiť dá môžete kedykoľvek. Dostávajte do schránky rady, oznámenia a príležitosti na výskum od spoločnosti Bitwarden. Kedykoľvek sa môžete odhlásiť z odberu. - Súkromie na prvom mieste + Súkromie na prvom mieste Ukladajte prihlasovacie údaje, karty a identity do zabezpečeného trezoru. Bitwarden používa šifrovanie s nulovou znalosťou od začiatku do konca na ochranu toho, čo je pre vás dôležité. Nastavte si odomykanie biometrickými údajmi a automatické vypĺňanie na prihlasovanie do účtov bez zadávania jediného písmena. Rýchle a jednoduché prihlásenie diff --git a/app/src/main/res/values-sl-rSI/strings.xml b/app/src/main/res/values-sl-rSI/strings.xml index 5ebf0eee4..835860092 100644 --- a/app/src/main/res/values-sl-rSI/strings.xml +++ b/app/src/main/res/values-sl-rSI/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index c2474c1a8..cda33d7c5 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -953,7 +953,7 @@ Или пријавите се, можда већ имате налог. Добијајте имјл од Bitwarden-а за најаве, савете и могућности истраживања. Откажите претплату у било ком тренутку. Добијајте савете, најаве и могућности истраживања од Bitwarden-а у пријемном сандучету. Откажите претплату у било ком тренутку. - Приватност, приоритет + Приватност, приоритет Сачувајте пријаве, картице и идентитете у свој безбедни сеф. Bitwarden користи шифровање од почетка-до-краја да заштити оно што вам је важно. Подесите биометријско откључавање и аутоматско попуњавање да бисте се пријавили на своје налоге без уноса ниједног слова. Брза и лака пријава diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index b17fa0d3b..3a51e29c2 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -221,7 +221,7 @@ Ange den 6-siffriga verifieringskoden från din autentiseringsapp. Ange den 6-siffriga verifieringskoden som skickades till %1$s. Inloggning ej tillgänglig - Detta konto har tvåstegsverifiering aktiverat, men ingen av de konfigurerade metoderna stöds av den här enheten. + Detta konto har tvåstegsverifiering aktiverat, men ingen av de konfigurerade metoderna stöds av den här enheten. Vänligen använd en enhet som stöds och/eller lägg till fler metoder som har bättre stöd bland enheter (t.ex. en autentiseringsapp). Återställningskod Kom ihåg mig @@ -953,7 +953,7 @@ Vill du byta till detta konto? Eller logga in, du kanske redan har ett konto. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-ta-rIN/strings.xml b/app/src/main/res/values-ta-rIN/strings.xml index 4b0d622a7..5e491206a 100644 --- a/app/src/main/res/values-ta-rIN/strings.xml +++ b/app/src/main/res/values-ta-rIN/strings.xml @@ -952,7 +952,7 @@ Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-te-rIN/strings.xml b/app/src/main/res/values-te-rIN/strings.xml index 4a5773e9f..cfbdae808 100644 --- a/app/src/main/res/values-te-rIN/strings.xml +++ b/app/src/main/res/values-te-rIN/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index 048e5cb16..24617d9a3 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -952,7 +952,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index 90f9ec345..90f0f0cb3 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -951,7 +951,7 @@ Bu hesaba geçmek ister misiniz? Veya zaten hesabınız varsa giriş yapın. Bitwarden\'dan duyurular, öneriler ve araştırmalarla ilgili e-postalar alın. İstediğiniz zaman aboneliğinizi iptal edebilirsiniz. Bitwarden\'dan öneriler, duyurular ve araştırma fırsatları e-posta adresinize gelsin. İstediğiniz zaman aboneliğinizi iptal edebilirsiniz. - Önce gizlilik + Önce gizlilik Hesaplarınızı, kartlarınızı ve kimliklerinizi güvenli kasanıza kaydedin. Bitwarden\'ın sıfır bilgi ispatlı uçtan uca şifrelemesi sizin için önemli olan her şeyi korur. Hesaplarınıza parola yazmadan giriş yapmak için biyometrik kilit açmayı ayarlayabilirsiniz. Hızlı ve kolay giriş diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 5ad86c126..7176cc5e4 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -952,7 +952,7 @@ Або увійдіть, якщо вже маєте обліковий запис. Отримуйте електронні листи від Bitwarden з оголошеннями, порадами та інформацією про нові можливості. Відписатися можна будь-коли. Отримуйте поради, оголошення та інформацію щодо можливості участі в дослідженнях від Bitwarden у вашій поштовій скриньці. Відпишіться коли завгодно. - Приватність понад усе + Приватність понад усе Зберігайте записи входу, картки та посвідчення в захищеному сховищі. Для захисту ваших даних Bitwarden використовує наскрізне шифрування з нульовим рівнем доступу. Налаштуйте біометричне розблокування та автозаповнення, щоб миттєво входити в облікові записи. Швидкий і легкий вхід diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index 7654dc5d0..2358b529e 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -952,7 +952,7 @@ Bạn có muốn chuyển sang tài khoản này không? Hoặc đăng nhập, nếu bạn đã có tài khoản. Nhận email từ Bitwarden để nhận thông báo, đề xuất và cơ hội nghiên cứu. Hủy đăng ký bất cứ lúc nào. Nhận đề xuất, thông báo và cơ hội nghiên cứu từ Bitwarden trong hộp thư đến của bạn. Hủy đăng ký bất cứ lúc nào. - Bảo mật, riêng tư + Bảo mật, riêng tư Lưu thông tin đăng nhập, thẻ tín dụng và danh tính vào kho lưu trữ an toàn của bạn. Bitwarden sử dụng mã hóa đầu cuối không cần kiến ​​thức để bảo vệ những gì quan trọng với bạn. Thiết lập mở khóa sinh trắc học và tự động điền để đăng nhập vào tài khoản của bạn mà không cần nhập tay. Đăng nhập nhanh và dễ dàng diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2a1760d4c..41ef820a9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -952,7 +952,7 @@ 或者登录,您可能已经有一个账户了。 获取来自 Bitwarden 的建议、公告和调研电子邮件。随时退订。 获取来自 Bitwarden 的建议、公告和调研电子邮件。随时退订。 - 隐私优先 + 隐私优先 将登录、支付卡和身份保存到您的安全密码库。Bitwarden 使用零知识、端到端的加密来保护您的重要信息。 设置生物识别解锁和自动填充,无需输入任何字符即可登录您的账户。 快速便捷的登录 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 1e48e6639..2e2802d17 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -952,7 +952,7 @@ Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Privacy, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 66887f45d..d8cd049c8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,7 +41,6 @@ Invalid master password. Try again. Invalid PIN. Try again. Launch - Log In Log in Login Log out @@ -961,7 +960,7 @@ Do you want to switch to this account? Or log in, you may already have an account. Get emails from Bitwarden for announcements, advice, and research opportunities. Unsubscribe at any time. Get advice, announcements, and research opportunities from Bitwarden in your inbox. Unsubscribe at any time. - Privacy, prioritized + Security, prioritized Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you. Set up biometric unlock and autofill to log into your accounts without typing a single letter. Quick and easy login @@ -1097,4 +1096,12 @@ Do you want to switch to this account? Skip for now Done %1$s of %2$s + Copy identity name + Copy company + Copy social security number + Copy passport number + Copy license number + Copy email + Copy phone number + Copy address diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt index 90d64d205..30d4fa39b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/network/service/IdentityServiceTest.kt @@ -13,6 +13,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson +import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson @@ -370,14 +371,19 @@ class IdentityServiceTest : BaseServiceTest() { runTest { server.enqueue(MockResponse().setResponseCode(200).setBody(EMAIL_TOKEN)) val result = identityService.sendVerificationEmail(SEND_VERIFICATION_EMAIL_REQUEST) - assertEquals(JsonPrimitive(EMAIL_TOKEN).content.asSuccess(), result) + assertEquals( + SendVerificationEmailResponseJson + .Success(JsonPrimitive(EMAIL_TOKEN).content) + .asSuccess(), + result, + ) } @Test fun `sendVerificationEmail should return null when response is empty success`() = runTest { server.enqueue(MockResponse().setResponseCode(204)) val result = identityService.sendVerificationEmail(SEND_VERIFICATION_EMAIL_REQUEST) - assertEquals(null.asSuccess(), result) + assertEquals(SendVerificationEmailResponseJson.Success(null).asSuccess(), result) } @Test @@ -422,7 +428,6 @@ class IdentityServiceTest : BaseServiceTest() { ) } - @Suppress("MaxLineLength") @Test fun `verifyEmailToken should return Invalid when response message is non expired error`() = runTest { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index 5e0882b33..3aae102b4 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -38,6 +38,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJs import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson +import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailResponseJson import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod @@ -6043,7 +6044,7 @@ class AuthRepositoryTest { receiveMarketingEmails = true, ), ) - } returns EMAIL_VERIFICATION_TOKEN.asSuccess() + } returns SendVerificationEmailResponseJson.Success(EMAIL_VERIFICATION_TOKEN).asSuccess() val result = repository.sendVerificationEmail( email = EMAIL, @@ -6056,6 +6057,32 @@ class AuthRepositoryTest { ) } + @Test + fun `sendVerificationEmail success with invalid email should return error`() = runTest { + val errorMessage = "Failure" + coEvery { + identityService.sendVerificationEmail( + SendVerificationEmailRequestJson( + email = EMAIL, + name = NAME, + receiveMarketingEmails = true, + ), + ) + } returns SendVerificationEmailResponseJson + .Invalid(invalidMessage = errorMessage, validationErrors = null) + .asSuccess() + + val result = repository.sendVerificationEmail( + email = EMAIL, + name = NAME, + receiveMarketingEmails = true, + ) + assertEquals( + SendVerificationEmailResult.Error(errorMessage = errorMessage), + result, + ) + } + @Test fun `sendVerificationEmail failure should return success if body null`() = runTest { coEvery { @@ -6066,7 +6093,7 @@ class AuthRepositoryTest { receiveMarketingEmails = true, ), ) - } returns null.asSuccess() + } returns SendVerificationEmailResponseJson.Success(null).asSuccess() val result = repository.sendVerificationEmail( email = EMAIL, @@ -6089,7 +6116,7 @@ class AuthRepositoryTest { receiveMarketingEmails = true, ), ) - } returns EMAIL_VERIFICATION_TOKEN.asSuccess() + } returns SendVerificationEmailResponseJson.Success(EMAIL_VERIFICATION_TOKEN).asSuccess() val result = repository.sendVerificationEmail( email = EMAIL, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt index d92e4a965..915746004 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnScreenTest.kt @@ -60,7 +60,7 @@ class EnterpriseSignOnScreenTest : BaseComposeTest() { @Test fun `app bar log in click should send LogInClick action`() { - composeTestRule.onNodeWithText("Log In").performClick() + composeTestRule.onNodeWithText("Log in").performClick() verify { viewModel.trySendAction(EnterpriseSignOnAction.LogInClick) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt index b99a23f4a..c6bcf8fc5 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/welcome/WelcomeScreenTest.kt @@ -41,13 +41,13 @@ class WelcomeScreenTest : BaseComposeTest() { @Test fun `pages should display and update according to state`() { composeTestRule - .onNodeWithText("Privacy, prioritized") + .onNodeWithText("Security, prioritized") .assertExists() .assertIsDisplayed() mutableEventFlow.tryEmit(WelcomeEvent.UpdatePager(index = 1)) composeTestRule - .onNodeWithText("Privacy, prioritized") + .onNodeWithText("Security, prioritized") .assertDoesNotExist() composeTestRule .onNodeWithText("Quick and easy login") @@ -65,13 +65,13 @@ class WelcomeScreenTest : BaseComposeTest() { @Test fun `pages should display and update according to state in landscape mode`() { composeTestRule - .onNodeWithText("Privacy, prioritized") + .onNodeWithText("Security, prioritized") .assertExists() .assertIsDisplayed() mutableEventFlow.tryEmit(WelcomeEvent.UpdatePager(index = 1)) composeTestRule - .onNodeWithText("Privacy, prioritized") + .onNodeWithText("Security, prioritized") .assertDoesNotExist() composeTestRule .onNodeWithText("Quick and easy login") @@ -111,7 +111,7 @@ class WelcomeScreenTest : BaseComposeTest() { // will be in view on the UI testing viewport. mutableStateFlow.update { it.copy(pages = emptyList()) } composeTestRule - .onNodeWithText("Log In") + .onNodeWithText("Log in") .performClick() verify { viewModel.trySendAction(WelcomeAction.LoginClick) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt index 0c71c774f..f63b90561 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onChildren import androidx.compose.ui.test.onLast import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onSiblings import androidx.compose.ui.test.performClick @@ -1257,6 +1258,114 @@ class VaultItemScreenTest : BaseComposeTest() { .filterToOne(hasAnyAncestor(isPopup())) .assertDoesNotExist() } + + @Test + fun `on login copy notes field click should send CopyNotesClick`() { + + mutableStateFlow.update { currentState -> + currentState.copy( + viewState = DEFAULT_LOGIN_VIEW_STATE, + ) + } + composeTestRule.onNodeWithTextAfterScroll("Lots of notes") + composeTestRule + .onNodeWithTag("CipherNotesCopyButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick) + } + } + + @Test + fun `on identity copy notes field click should send CopyNotesClick`() { + // Adding a custom field so that we can scroll to it + // So we can see the Copy notes button but not have it covered by the FAB + val textField = VaultItemState.ViewState.Content.Common.Custom.TextField( + name = "text", + value = "value", + isCopyable = true, + ) + + EMPTY_VIEW_STATES + .forEach { typeState -> + mutableStateFlow.update { currentState -> + currentState.copy( + viewState = typeState.copy( + type = DEFAULT_IDENTITY, + common = EMPTY_COMMON.copy( + notes = "this is a note", + customFields = listOf(textField), + ), + ), + ) + } + + composeTestRule.onNodeWithTextAfterScroll(textField.name) + composeTestRule + .onNodeWithTag("CipherNotesCopyButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick) + } + } + } + + @Test + fun `on card copy notes field click should send CopyNotesClick`() { + // Adding a custom field so that we can scroll to it + // So we can see the Copy notes button but not have it covered by the FAB + val textField = VaultItemState.ViewState.Content.Common.Custom.TextField( + name = "text", + value = "value", + isCopyable = true, + ) + + EMPTY_VIEW_STATES + .forEach { typeState -> + mutableStateFlow.update { currentState -> + currentState.copy( + viewState = typeState.copy( + type = DEFAULT_IDENTITY, + common = EMPTY_COMMON.copy( + notes = "this is a note", + customFields = listOf(textField), + ), + ), + ) + } + } + + composeTestRule.onNodeWithTextAfterScroll(textField.name) + + composeTestRule + .onNodeWithTag("CipherNotesCopyButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick) + } + } + + @Test + fun `on secure note copy notes field click should send CopyNotesClick`() { + + mutableStateFlow.update { currentState -> + currentState.copy( + viewState = DEFAULT_SECURE_NOTE_VIEW_STATE, + ) + } + composeTestRule.onNodeWithTextAfterScroll("Lots of notes") + + composeTestRule + .onNodeWithTag("CipherNotesCopyButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick) + } + } //endregion common //region login @@ -1927,6 +2036,144 @@ class VaultItemScreenTest : BaseComposeTest() { composeTestRule.assertScrollableNodeDoesNotExist(identityName) } + + @Test + fun `in identity state, on copy identity name field click should send CopyIdentityNameClick`() { + + val identityName = "the identity name" + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onNodeWithTextAfterScroll(identityName) + + composeTestRule + .onNodeWithTag("IdentityCopyNameButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyIdentityNameClick) + } + } + + @Test + fun `in identity state, on copy username field click should send CopyUsernameClick`() { + val username = "the username" + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onNodeWithTextAfterScroll(username) + + composeTestRule + .onNodeWithTag("IdentityCopyUsernameButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyUsernameClick) + } + } + + @Test + fun `in identity state, on copy company field click should send CopyCompanyClick`() { + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + + // Scroll to ssn so we can see the Copy company button but not have it covered by the FAB + composeTestRule.onNodeWithTextAfterScroll("the SSN") + + composeTestRule + .onNodeWithTag("IdentityCopyCompanyButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyCompanyClick) + } + } + + @Test + fun `in identity state, on copy SSN field click should send CopySsnClick`() { + val ssn = "the SSN" + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onNodeWithTextAfterScroll(ssn) + + composeTestRule + .onNodeWithTag("IdentityCopySsnButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopySsnClick) + } + } + + @Suppress("MaxLineLength") + @Test + fun `in identity state, on copy passport number field click should send CopyPassportNumberClick`() { + val passportNumber = "the passport number" + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onNodeWithTextAfterScroll(passportNumber) + + composeTestRule + .onNodeWithTag("IdentityCopyPassportNumberButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPassportNumberClick) + } + } + + @Suppress("MaxLineLength") + @Test + fun `in identity state, on copy license number field click should send CopyLicenseNumberClick`() { + val licenseNumber = "the license number" + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onNodeWithTextAfterScroll(licenseNumber) + + composeTestRule + .onNodeWithTag("IdentityCopyLicenseNumberButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyLicenseNumberClick) + } + } + + @Test + fun `in identity state, on copy email field click should send CopyEmailClick`() { + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onFirstNodeWithTextAfterScroll("the address") + + composeTestRule + .onNodeWithContentDescriptionAfterScroll("Copy email") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyEmailClick) + } + } + + @Test + fun `in identity state, on copy phone field click should send CopyPhoneClick`() { + val phone = "the phone number" + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onNodeWithTextAfterScroll(phone) + + composeTestRule + .onNodeWithTag("IdentityCopyPhoneButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPhoneClick) + } + } + + @Test + fun `in identity state, on copy address field click should send CopyAddressClick`() { + val address = "the address" + mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) } + composeTestRule.onNodeWithTextAfterScroll(address) + + composeTestRule + .onNodeWithTag("IdentityCopyAddressButton") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyAddressClick) + } + } //endregion identity //region card diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt index 89485e7a6..4a82e0c32 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt @@ -1670,6 +1670,33 @@ class VaultItemViewModelTest : BaseViewModelTest() { coVerify { mockFileManager.delete(file) } } + + @Test + fun `on CopyNotesFieldClick should call setText on ClipboardManager`() { + every { + mockCipherView.toViewState( + previousState = null, + isPremiumUser = true, + hasMasterPassword = true, + totpCodeItemData = null, + canDelete = true, + canAssignToCollections = true, + ) + } returns DEFAULT_VIEW_STATE + + mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView) + mutableAuthCodeItemFlow.value = DataState.Loaded(data = null) + mutableCollectionsStateFlow.value = DataState.Loaded(emptyList()) + + val notes = "Lots of notes" + every { clipboardManager.setText(text = notes) } just runs + + viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick) + + verify(exactly = 1) { + clipboardManager.setText(text = notes) + } + } } @Nested @@ -2527,6 +2554,143 @@ class VaultItemViewModelTest : BaseViewModelTest() { } } + @Nested + inner class IdentityActions { + private lateinit var viewModel: VaultItemViewModel + + @BeforeEach + fun setup() { + viewModel = createViewModel( + state = DEFAULT_STATE.copy( + viewState = IDENTITY_VIEW_STATE, + ), + ) + every { + mockCipherView.toViewState( + previousState = null, + isPremiumUser = true, + hasMasterPassword = true, + totpCodeItemData = null, + canDelete = true, + canAssignToCollections = true, + ) + } returns IDENTITY_VIEW_STATE + mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView) + mutableAuthCodeItemFlow.value = DataState.Loaded(data = null) + mutableCollectionsStateFlow.value = DataState.Loaded(emptyList()) + } + + @Test + fun `on CopyIdentityNameClick should copy fingerprint to clipboard`() = + runTest { + val username = "the username" + every { clipboardManager.setText(text = username) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyUsernameClick) + + verify(exactly = 1) { + clipboardManager.setText(text = username) + } + } + + @Test + fun `on CopyUsernameClick should copy fingerprint to clipboard`() = + runTest { + val identityName = "the identity name" + every { clipboardManager.setText(text = identityName) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyIdentityNameClick) + + verify(exactly = 1) { + clipboardManager.setText(text = identityName) + } + } + + @Test + fun `on CopyCompanyClick should copy company to clipboard`() = runTest { + val company = "the company name" + every { clipboardManager.setText(text = company) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyCompanyClick) + + verify(exactly = 1) { + clipboardManager.setText(text = company) + } + } + + @Test + fun `on CopySsnClick should copy SSN to clipboard`() = runTest { + val ssn = "the SSN" + every { clipboardManager.setText(text = ssn) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopySsnClick) + + verify(exactly = 1) { + clipboardManager.setText(text = ssn) + } + } + + @Test + fun `on CopyPassportNumberClick should copy passport number to clipboard`() = runTest { + val passportNumber = "the passport number" + every { clipboardManager.setText(text = passportNumber) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPassportNumberClick) + + verify(exactly = 1) { + clipboardManager.setText(text = passportNumber) + } + } + + @Test + fun `on CopyLicenseNumberClick should copy license number to clipboard`() = runTest { + val licenseNumber = "the license number" + every { clipboardManager.setText(text = licenseNumber) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyLicenseNumberClick) + + verify(exactly = 1) { + clipboardManager.setText(text = licenseNumber) + } + } + + @Test + fun `on CopyEmailClick should copy email to clipboard`() = runTest { + val email = "the email address" + every { clipboardManager.setText(text = email) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyEmailClick) + + verify(exactly = 1) { + clipboardManager.setText(text = email) + } + } + + @Test + fun `on CopyPhoneClick should copy phone to clipboard`() = runTest { + val phone = "the phone number" + every { clipboardManager.setText(text = phone) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPhoneClick) + + verify(exactly = 1) { + clipboardManager.setText(text = phone) + } + } + + @Test + fun `on CopyAddressClick should copy address to clipboard`() = runTest { + val address = "the address" + every { clipboardManager.setText(text = address) } just runs + + viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyAddressClick) + + verify(exactly = 1) { + clipboardManager.setText(text = address) + } + } + } + @Nested inner class VaultItemFlow { @BeforeEach @@ -2844,6 +3008,19 @@ class VaultItemViewModelTest : BaseViewModelTest() { showPrivateKey = false, ) + private val DEFAULT_IDENTITY_TYPE: VaultItemState.ViewState.Content.ItemType.Identity = + VaultItemState.ViewState.Content.ItemType.Identity( + username = "the username", + identityName = "the identity name", + company = "the company name", + ssn = "the SSN", + passportNumber = "the passport number", + licenseNumber = "the license number", + email = "the email address", + phone = "the phone number", + address = "the address", + ) + private val DEFAULT_COMMON: VaultItemState.ViewState.Content.Common = VaultItemState.ViewState.Content.Common( name = "login cipher", @@ -2908,5 +3085,11 @@ class VaultItemViewModelTest : BaseViewModelTest() { common = DEFAULT_COMMON, type = DEFAULT_SSH_KEY_TYPE, ) + + private val IDENTITY_VIEW_STATE: VaultItemState.ViewState.Content = + VaultItemState.ViewState.Content( + common = DEFAULT_COMMON, + type = DEFAULT_IDENTITY_TYPE, + ) } }