mirror of
https://github.com/bitwarden/android.git
synced 2026-10-22 02:53:19 -05:00
PM-14179: Create and apply card style to UI (#4567)
This commit is contained in:
1 parent
2672f426dc
commit
5840bcdf85
110 files changed
+3442
-2010
No files matched your search
+2
@@ -42,6 +42,7 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.image.BitwardenGifImage
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -167,6 +168,7 @@ private fun SetupAutoFillContent(
|
||||
),
|
||||
isChecked = state.autofillEnabled,
|
||||
onCheckedChange = onAutofillServiceChanged,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
|
||||
+10
-1
@@ -46,11 +46,13 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenUnlockWithBiometricsSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenUnlockWithPinSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalBiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricSupportStatus
|
||||
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.platform.util.isPortrait
|
||||
@@ -156,11 +158,13 @@ private fun SetupUnlockScreenContent(
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
val biometricSupportStatus = biometricsManager.biometricSupportStatus
|
||||
BitwardenUnlockWithBiometricsSwitch(
|
||||
biometricSupportStatus = biometricsManager.biometricSupportStatus,
|
||||
biometricSupportStatus = biometricSupportStatus,
|
||||
isChecked = state.isUnlockWithBiometricsEnabled || showBiometricsPrompt,
|
||||
onDisableBiometrics = handler.onDisableBiometrics,
|
||||
onEnableBiometrics = handler.onEnableBiometrics,
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.testTag(tag = "UnlockWithBiometricsSwitch")
|
||||
.fillMaxWidth()
|
||||
@@ -170,6 +174,11 @@ private fun SetupUnlockScreenContent(
|
||||
isUnlockWithPasswordEnabled = state.isUnlockWithPasswordEnabled,
|
||||
isUnlockWithPinEnabled = state.isUnlockWithPinEnabled,
|
||||
onUnlockWithPinToggleAction = handler.onUnlockWithPinToggle,
|
||||
cardStyle = if (biometricSupportStatus == BiometricSupportStatus.NOT_SUPPORTED) {
|
||||
CardStyle.Full
|
||||
} else {
|
||||
CardStyle.Bottom
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag(tag = "UnlockWithPinSwitch")
|
||||
.fillMaxWidth()
|
||||
|
||||
+44
-33
@@ -5,6 +5,7 @@ import android.widget.Toast
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -52,6 +53,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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
|
||||
@@ -162,6 +164,7 @@ fun CompleteRegistrationScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
CompleteRegistrationContent(
|
||||
passwordInput = state.passwordInput,
|
||||
passwordStrengthState = state.passwordStrengthState,
|
||||
@@ -175,6 +178,7 @@ fun CompleteRegistrationScreen(
|
||||
showNewOnboardingUi = state.onboardingEnabled,
|
||||
userEmail = state.userEmail,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -200,7 +204,6 @@ private fun CompleteRegistrationContent(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
if (showNewOnboardingUi) {
|
||||
CompleteRegistrationContentHeader(
|
||||
modifier = Modifier
|
||||
@@ -242,23 +245,22 @@ private fun CompleteRegistrationContent(
|
||||
showPasswordChange = { showPassword = it },
|
||||
value = passwordInput,
|
||||
onValueChange = handler.onPasswordInputChange,
|
||||
hint = stringResource(id = R.string.master_password_important_hint)
|
||||
.takeIf { !showNewOnboardingUi },
|
||||
showPasswordTestTag = "PasswordVisibilityToggle",
|
||||
imeAction = ImeAction.Next,
|
||||
supportingTextContent = {
|
||||
PasswordStrengthIndicator(
|
||||
state = passwordStrengthState,
|
||||
currentCharacterCount = passwordInput.length,
|
||||
minimumCharacterCount = minimumPasswordLength.takeIf { showNewOnboardingUi },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Top(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordEntry")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
showPasswordTestTag = "PasswordVisibilityToggle",
|
||||
imeAction = ImeAction.Next,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
PasswordStrengthIndicator(
|
||||
state = passwordStrengthState,
|
||||
currentCharacterCount = passwordInput.length,
|
||||
minimumCharacterCount = minimumPasswordLength.takeIf { showNewOnboardingUi },
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(
|
||||
id = R.string.retype_master_password_required
|
||||
@@ -269,13 +271,13 @@ private fun CompleteRegistrationContent(
|
||||
showPassword = showPassword,
|
||||
showPasswordChange = { showPassword = it },
|
||||
onValueChange = handler.onConfirmPasswordInputChange,
|
||||
showPasswordTestTag = "ConfirmPasswordVisibilityToggle",
|
||||
cardStyle = CardStyle.Middle(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("ConfirmMasterPasswordEntry")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
showPasswordTestTag = "ConfirmPasswordVisibilityToggle",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(
|
||||
id = R.string.master_password_hint_not_specified
|
||||
@@ -284,37 +286,46 @@ private fun CompleteRegistrationContent(
|
||||
),
|
||||
value = passwordHintInput,
|
||||
onValueChange = handler.onPasswordHintChange,
|
||||
hint = stringResource(
|
||||
id = R.string.bitwarden_cannot_recover_a_lost_or_forgotten_master_password
|
||||
.takeIf { showNewOnboardingUi }
|
||||
?: R.string.master_password_hint_description,
|
||||
),
|
||||
supportingTextContent = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.bitwarden_cannot_recover_a_lost_or_forgotten_master_password
|
||||
.takeIf { showNewOnboardingUi }
|
||||
?: R.string.master_password_hint_description,
|
||||
),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
if (showNewOnboardingUi) {
|
||||
BitwardenClickableText(
|
||||
label = stringResource(
|
||||
id = R.string.learn_about_other_ways_to_prevent_account_lockout,
|
||||
),
|
||||
onClick = handler.onLearnToPreventLockout,
|
||||
style = BitwardenTheme.typography.labelMedium,
|
||||
innerPadding = PaddingValues(vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
)
|
||||
if (showNewOnboardingUi) {
|
||||
BitwardenClickableText(
|
||||
label = stringResource(
|
||||
id = R.string.learn_about_other_ways_to_prevent_account_lockout,
|
||||
),
|
||||
onClick = handler.onLearnToPreventLockout,
|
||||
style = BitwardenTheme.typography.labelMedium,
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.check_known_data_breaches_for_this_password),
|
||||
isChecked = isCheckDataBreachesToggled,
|
||||
onCheckedChange = handler.onCheckDataBreachesToggle,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("CheckExposedMasterPasswordToggle")
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
if (showNewOnboardingUi) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenFilledButton(
|
||||
label = callToActionText,
|
||||
isEnabled = nextButtonEnabled,
|
||||
|
||||
+3
-1
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.graphics.TransformOrigin
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
@@ -37,9 +38,9 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod", "MagicNumber")
|
||||
@Composable
|
||||
fun PasswordStrengthIndicator(
|
||||
modifier: Modifier = Modifier,
|
||||
state: PasswordStrengthState,
|
||||
currentCharacterCount: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
minimumCharacterCount: Int? = null,
|
||||
) {
|
||||
val minimumRequirementMet = (minimumCharacterCount == null) ||
|
||||
@@ -86,6 +87,7 @@ fun PasswordStrengthIndicator(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(4.dp)
|
||||
.clip(shape = BitwardenTheme.shapes.progressIndicator)
|
||||
.background(BitwardenTheme.colorScheme.sliderButton.unfilled),
|
||||
) {
|
||||
Box(
|
||||
|
||||
+41
-28
@@ -53,6 +53,7 @@ import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountAction.Ter
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountEvent.NavigateToPrivacyPolicy
|
||||
import com.x8bit.bitwarden.ui.auth.feature.createaccount.CreateAccountEvent.NavigateToTerms
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
@@ -60,6 +61,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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
|
||||
@@ -179,43 +181,50 @@ fun CreateAccountScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.email_address),
|
||||
value = state.emailInput,
|
||||
onValueChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(EmailInputChange(it)) }
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
keyboardType = KeyboardType.Email,
|
||||
textFieldTestTag = "EmailAddressEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
var showPassword by rememberSaveable { mutableStateOf(false) }
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.master_password),
|
||||
showPassword = showPassword,
|
||||
showPasswordChange = { showPassword = it },
|
||||
value = state.passwordInput,
|
||||
hint = state.passwordLengthLabel(),
|
||||
onValueChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(PasswordInputChange(it)) }
|
||||
},
|
||||
showPasswordTestTag = "PasswordVisibilityToggle",
|
||||
supportingTextContent = {
|
||||
PasswordStrengthIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = state.passwordStrengthState,
|
||||
currentCharacterCount = state.passwordInput.length,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = state.passwordLengthLabel(),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Top(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
showPasswordTestTag = "PasswordVisibilityToggle",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
PasswordStrengthIndicator(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
state = state.passwordStrengthState,
|
||||
currentCharacterCount = state.passwordInput.length,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.retype_master_password),
|
||||
value = state.confirmPasswordInput,
|
||||
@@ -224,26 +233,27 @@ fun CreateAccountScreen(
|
||||
onValueChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(ConfirmPasswordInputChange(it)) }
|
||||
},
|
||||
showPasswordTestTag = "ConfirmPasswordVisibilityToggle",
|
||||
cardStyle = CardStyle.Middle(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("ConfirmMasterPasswordEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
showPasswordTestTag = "ConfirmPasswordVisibilityToggle",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.master_password_hint),
|
||||
value = state.passwordHintInput,
|
||||
onValueChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(PasswordHintChange(it)) }
|
||||
},
|
||||
hint = stringResource(id = R.string.master_password_hint_description),
|
||||
supportingText = stringResource(id = R.string.master_password_hint_description),
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.check_known_data_breaches_for_this_password),
|
||||
isChecked = state.isCheckDataBreachesToggled,
|
||||
@@ -252,11 +262,12 @@ fun CreateAccountScreen(
|
||||
viewModel.trySendAction(CheckDataBreachesToggle(newState = newState))
|
||||
}
|
||||
},
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.testTag("CheckExposedMasterPasswordToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
TermsAndPrivacySwitch(
|
||||
isChecked = state.isAcceptPoliciesToggled,
|
||||
onCheckedChange = remember(viewModel) {
|
||||
@@ -270,8 +281,9 @@ fun CreateAccountScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -292,7 +304,7 @@ private fun TermsAndPrivacySwitch(
|
||||
isChecked = isChecked,
|
||||
contentDescription = "AcceptPoliciesToggle",
|
||||
onCheckedChange = onCheckedChange,
|
||||
subContent = {
|
||||
supportingTextContent = {
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -301,7 +313,7 @@ private fun TermsAndPrivacySwitch(
|
||||
label = stringResource(id = R.string.terms_of_service),
|
||||
onClick = onTermsClick,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
innerPadding = PaddingValues(vertical = 4.dp, horizontal = 0.dp),
|
||||
innerPadding = PaddingValues(vertical = 8.dp, horizontal = 0.dp),
|
||||
)
|
||||
Text(
|
||||
text = ",",
|
||||
@@ -314,9 +326,10 @@ private fun TermsAndPrivacySwitch(
|
||||
label = stringResource(id = R.string.privacy_policy),
|
||||
onClick = onPrivacyPolicyClick,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
innerPadding = PaddingValues(vertical = 4.dp, horizontal = 0.dp),
|
||||
innerPadding = PaddingValues(vertical = 8.dp, horizontal = 0.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
)
|
||||
}
|
||||
+9
-4
@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -27,11 +27,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
@@ -144,28 +146,31 @@ private fun EnterpriseSignOnScreenContent(
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.log_in_sso_summary),
|
||||
textAlign = TextAlign.Start,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
value = state.orgIdentifierInput,
|
||||
onValueChange = onOrgIdentifierInputChange,
|
||||
label = stringResource(id = R.string.org_identifier),
|
||||
textFieldTestTag = "OrgSSOIdentifierEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
+28
-17
@@ -29,11 +29,13 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.BuildConfig
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -100,20 +102,22 @@ fun EnvironmentScreen(
|
||||
.imePadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.self_hosted_environment),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.server_url),
|
||||
value = state.serverUrl,
|
||||
placeholder = "ex. https://bitwarden.company.com",
|
||||
hint = stringResource(id = R.string.self_hosted_environment_footer),
|
||||
supportingText = stringResource(id = R.string.self_hosted_environment_footer),
|
||||
onValueChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(EnvironmentAction.ServerUrlChange(it)) }
|
||||
},
|
||||
@@ -127,22 +131,24 @@ fun EnvironmentScreen(
|
||||
persistentListOf()
|
||||
},
|
||||
keyboardType = KeyboardType.Uri,
|
||||
textFieldTestTag = "ServerUrlEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ServerUrlEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_environment),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.web_vault_url),
|
||||
@@ -151,13 +157,14 @@ fun EnvironmentScreen(
|
||||
{ viewModel.trySendAction(EnvironmentAction.WebVaultServerUrlChange(it)) }
|
||||
},
|
||||
keyboardType = KeyboardType.Uri,
|
||||
textFieldTestTag = "WebVaultUrlEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "WebVaultUrlEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.api_url),
|
||||
@@ -166,13 +173,14 @@ fun EnvironmentScreen(
|
||||
{ viewModel.trySendAction(EnvironmentAction.ApiServerUrlChange(it)) }
|
||||
},
|
||||
keyboardType = KeyboardType.Uri,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("ApiUrlEntry")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.identity_url),
|
||||
@@ -181,13 +189,14 @@ fun EnvironmentScreen(
|
||||
{ viewModel.trySendAction(EnvironmentAction.IdentityServerUrlChange(it)) }
|
||||
},
|
||||
keyboardType = KeyboardType.Uri,
|
||||
textFieldTestTag = "IdentityUrlEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityUrlEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.icons_url),
|
||||
@@ -195,14 +204,16 @@ fun EnvironmentScreen(
|
||||
onValueChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(EnvironmentAction.IconsServerUrlChange(it)) }
|
||||
},
|
||||
hint = stringResource(id = R.string.custom_environment_footer),
|
||||
supportingText = stringResource(id = R.string.custom_environment_footer),
|
||||
keyboardType = KeyboardType.Uri,
|
||||
textFieldTestTag = "IconsUrlEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IconsUrlEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
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
|
||||
@@ -39,6 +38,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.Environment
|
||||
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.account.BitwardenAccountSwitcher
|
||||
import com.x8bit.bitwarden.ui.platform.components.account.BitwardenPlaceholderAccountActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
@@ -48,6 +48,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
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.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -216,7 +217,7 @@ private fun LandingScreenContent(
|
||||
colorFilter = ColorFilter.tint(BitwardenTheme.colorScheme.icon.secondary),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.width(220.dp)
|
||||
.height(74.dp)
|
||||
.fillMaxWidth(),
|
||||
@@ -232,7 +233,7 @@ private fun LandingScreenContent(
|
||||
style = BitwardenTheme.typography.headlineSmall,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.standardHorizontalMargin()
|
||||
.wrapContentHeight(),
|
||||
)
|
||||
|
||||
@@ -242,38 +243,40 @@ private fun LandingScreenContent(
|
||||
|
||||
BitwardenTextField(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
value = state.emailInput,
|
||||
onValueChange = onEmailInputChange,
|
||||
label = stringResource(id = R.string.email_address),
|
||||
keyboardType = KeyboardType.Email,
|
||||
textFieldTestTag = "EmailAddressEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
supportingTextContent = {
|
||||
EnvironmentSelector(
|
||||
labelText = stringResource(id = R.string.logging_in_on),
|
||||
selectedOption = state.selectedEnvironmentType,
|
||||
onOptionSelected = onEnvironmentTypeSelect,
|
||||
modifier = Modifier
|
||||
.testTag("RegionSelectorDropdown")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
|
||||
EnvironmentSelector(
|
||||
labelText = stringResource(id = R.string.logging_in_on),
|
||||
selectedOption = state.selectedEnvironmentType,
|
||||
onOptionSelected = onEnvironmentTypeSelect,
|
||||
modifier = Modifier
|
||||
.testTag("RegionSelectorDropdown")
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.remember_me),
|
||||
isChecked = state.isRememberMeEnabled,
|
||||
onCheckedChange = onRememberMeToggle,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("RememberMeSwitch")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = R.string.continue_text),
|
||||
@@ -281,17 +284,17 @@ private fun LandingScreenContent(
|
||||
isEnabled = state.isContinueButtonEnabled,
|
||||
modifier = Modifier
|
||||
.testTag("ContinueButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
) {
|
||||
@@ -309,7 +312,7 @@ private fun LandingScreenContent(
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(58.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
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.ExperimentalMaterial3Api
|
||||
@@ -22,6 +21,7 @@ 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
|
||||
@@ -35,6 +35,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.account.BitwardenAccountSwitcher
|
||||
import com.x8bit.bitwarden.ui.platform.components.account.BitwardenPlaceholderAccountActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
@@ -45,6 +46,7 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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.util.rememberVectorPainter
|
||||
@@ -230,28 +232,35 @@ private fun LoginScreenContent(
|
||||
.imePadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenPasswordField(
|
||||
autoFocus = true,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordEntry")
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
value = state.passwordInput,
|
||||
onValueChange = onPasswordInputChanged,
|
||||
label = stringResource(id = R.string.master_password),
|
||||
showPasswordTestTag = "PasswordVisibilityToggle",
|
||||
)
|
||||
|
||||
BitwardenClickableText(
|
||||
label = stringResource(id = R.string.get_master_passwordword_hint),
|
||||
onClick = onMasterPasswordClick,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
supportingTextContent = {
|
||||
BitwardenClickableText(
|
||||
label = stringResource(id = R.string.get_master_passwordword_hint),
|
||||
onClick = onMasterPasswordClick,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
innerPadding = PaddingValues(
|
||||
top = 8.dp,
|
||||
bottom = 8.dp,
|
||||
start = 0.dp,
|
||||
end = 16.dp,
|
||||
),
|
||||
modifier = Modifier.testTag("GetMasterPasswordHintLabel"),
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.testTag("GetMasterPasswordHintLabel"),
|
||||
.testTag("MasterPasswordEntry")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = R.string.log_in_with_master_password),
|
||||
@@ -259,7 +268,7 @@ private fun LoginScreenContent(
|
||||
isEnabled = state.isLoginButtonEnabled,
|
||||
modifier = Modifier
|
||||
.testTag("LogInWithMasterPasswordButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -272,7 +281,7 @@ private fun LoginScreenContent(
|
||||
onClick = onLoginWithDeviceClick,
|
||||
modifier = Modifier
|
||||
.testTag("LogInWithAnotherDeviceButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -285,7 +294,7 @@ private fun LoginScreenContent(
|
||||
onClick = onSingleSignOnClick,
|
||||
modifier = Modifier
|
||||
.testTag("LogInWithSsoButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -297,12 +306,12 @@ private fun LoginScreenContent(
|
||||
state.emailAddress,
|
||||
state.environmentLabel,
|
||||
),
|
||||
textAlign = TextAlign.Start,
|
||||
textAlign = TextAlign.Center,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.testTag("LoggingInAsLabel")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -311,8 +320,12 @@ private fun LoginScreenContent(
|
||||
onClick = onNotYouButtonClick,
|
||||
style = BitwardenTheme.typography.labelLarge,
|
||||
innerPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp),
|
||||
modifier = Modifier.testTag("NotYouLabel"),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.testTag("NotYouLabel"),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -33,6 +33,7 @@ import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
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.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarData
|
||||
import com.x8bit.bitwarden.ui.platform.components.snackbar.BitwardenSnackbarHost
|
||||
@@ -122,6 +123,7 @@ fun MasterPasswordGeneratorScreen(
|
||||
},
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -137,7 +139,7 @@ private fun MasterPasswordGeneratorContent(
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenTextField(
|
||||
label = "",
|
||||
value = generatedPassword,
|
||||
@@ -146,6 +148,7 @@ private fun MasterPasswordGeneratorContent(
|
||||
shouldAddCustomLineBreaks = true,
|
||||
textStyle = BitwardenTheme.typography.sensitiveInfoSmall,
|
||||
visualTransformation = nonLetterColorVisualTransformation(),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
|
||||
+11
-16
@@ -1,11 +1,12 @@
|
||||
package com.x8bit.bitwarden.ui.auth.feature.masterpasswordhint
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -21,14 +22,15 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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
|
||||
|
||||
/**
|
||||
* The top level composable for the Login screen.
|
||||
@@ -107,9 +109,10 @@ fun MasterPasswordHintScreen(
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenTextField(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
value = state.emailInput,
|
||||
onValueChange = remember(viewModel) {
|
||||
@@ -118,19 +121,11 @@ fun MasterPasswordHintScreen(
|
||||
label = stringResource(id = R.string.email_address),
|
||||
keyboardType = KeyboardType.Email,
|
||||
textFieldTestTag = "MasterPasswordHintEmailField",
|
||||
supportingText = stringResource(id = R.string.enter_email_for_hint),
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.enter_email_for_hint),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
vertical = 4.dp,
|
||||
horizontal = 16.dp,
|
||||
),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-9
@@ -37,6 +37,7 @@ import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toAnnotatedString
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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
|
||||
@@ -190,15 +191,15 @@ private fun MainContent(
|
||||
),
|
||||
),
|
||||
)
|
||||
Column {
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.yes_i_can_reliably_access_my_email),
|
||||
isChecked = isEmailAccessEnabled,
|
||||
onCheckedChange = onEmailAccessToggleChanged,
|
||||
modifier = Modifier
|
||||
.testTag("EmailAccessToggle"),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.yes_i_can_reliably_access_my_email),
|
||||
isChecked = isEmailAccessEnabled,
|
||||
onCheckedChange = onEmailAccessToggleChanged,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("EmailAccessToggle"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -31,6 +31,7 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -87,7 +88,7 @@ private fun RemovePasswordScreenContent(
|
||||
modifier = modifier
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
|
||||
Text(
|
||||
text = state.description(),
|
||||
@@ -104,11 +105,12 @@ private fun RemovePasswordScreenContent(
|
||||
value = state.input,
|
||||
onValueChange = onInputChanged,
|
||||
showPasswordTestTag = "PasswordVisibilityToggle",
|
||||
autoFocus = true,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag(tag = "MasterPasswordEntry")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
autoFocus = true,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
|
||||
+20
-16
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
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.ExperimentalMaterial3Api
|
||||
@@ -29,6 +28,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.ForcePasswordResetReason
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
@@ -37,6 +37,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
|
||||
/**
|
||||
@@ -149,7 +150,7 @@ private fun ResetPasswordScreenContent(
|
||||
.imePadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
val instructionsTextId =
|
||||
if (state.resetReason == ForcePasswordResetReason.WEAK_MASTER_PASSWORD_ON_LOGIN) {
|
||||
R.string.update_weak_master_password_warning
|
||||
@@ -159,7 +160,7 @@ private fun ResetPasswordScreenContent(
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = instructionsTextId),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -174,7 +175,7 @@ private fun ResetPasswordScreenContent(
|
||||
BitwardenInfoCalloutCard(
|
||||
text = passwordPolicyContent,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -184,13 +185,12 @@ private fun ResetPasswordScreenContent(
|
||||
label = stringResource(id = R.string.current_master_password),
|
||||
value = state.currentPasswordInput,
|
||||
onValueChange = onCurrentPasswordInputChanged,
|
||||
cardStyle = CardStyle.Top(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordField")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
var isPasswordVisible by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -200,37 +200,41 @@ private fun ResetPasswordScreenContent(
|
||||
onValueChange = onPasswordInputChanged,
|
||||
showPassword = isPasswordVisible,
|
||||
showPasswordChange = { isPasswordVisible = it },
|
||||
cardStyle = if (
|
||||
state.resetReason == ForcePasswordResetReason.WEAK_MASTER_PASSWORD_ON_LOGIN) {
|
||||
CardStyle.Middle(dividerPadding = 0.dp)
|
||||
} else {
|
||||
CardStyle.Top(dividerPadding = 0.dp)
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("NewPasswordField")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.retype_master_password),
|
||||
value = state.retypePasswordInput,
|
||||
onValueChange = onRetypePasswordInputChanged,
|
||||
showPassword = isPasswordVisible,
|
||||
showPasswordChange = { isPasswordVisible = it },
|
||||
cardStyle = CardStyle.Middle(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("RetypePasswordField")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.master_password_hint),
|
||||
value = state.passwordHintInput,
|
||||
onValueChange = onPasswordHintInputChanged,
|
||||
hint = stringResource(id = R.string.master_password_hint_description),
|
||||
supportingText = stringResource(id = R.string.master_password_hint_description),
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
|
||||
+15
-13
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
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.ExperimentalMaterial3Api
|
||||
@@ -28,6 +27,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
@@ -35,6 +35,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -114,6 +115,7 @@ private fun SetPasswordScreenContent(
|
||||
modifier = modifier
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.your_organization_requires_you_to_set_a_master_password,
|
||||
@@ -121,7 +123,7 @@ private fun SetPasswordScreenContent(
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -130,7 +132,7 @@ private fun SetPasswordScreenContent(
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = R.string.reset_password_auto_enroll_invite_warning),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -143,40 +145,40 @@ private fun SetPasswordScreenContent(
|
||||
onValueChange = onPasswordInputChanged,
|
||||
showPassword = isPasswordVisible,
|
||||
showPasswordChange = { isPasswordVisible = it },
|
||||
hint = stringResource(id = R.string.master_password_description),
|
||||
supportingText = stringResource(id = R.string.master_password_description),
|
||||
cardStyle = CardStyle.Top(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("NewPasswordField")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.retype_master_password),
|
||||
value = state.retypePasswordInput,
|
||||
onValueChange = onRetypePasswordInputChanged,
|
||||
showPassword = isPasswordVisible,
|
||||
showPasswordChange = { isPasswordVisible = it },
|
||||
cardStyle = CardStyle.Middle(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.testTag("RetypePasswordField")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.master_password_hint),
|
||||
value = state.passwordHintInput,
|
||||
onValueChange = onPasswordHintInputChanged,
|
||||
hint = stringResource(id = R.string.master_password_hint_description),
|
||||
supportingText = stringResource(id = R.string.master_password_hint_description),
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "MasterPasswordHintLabel",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
+35
-32
@@ -59,6 +59,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
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.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -177,6 +178,7 @@ fun StartRegistrationScreen(
|
||||
isNewOnboardingUiEnabled = state.showNewOnboardingUi,
|
||||
handler = handler,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -195,7 +197,7 @@ private fun StartRegistrationContent(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
if (isNewOnboardingUiEnabled) {
|
||||
Image(
|
||||
painter = rememberVectorPainter(id = R.drawable.vault),
|
||||
@@ -210,50 +212,50 @@ private fun StartRegistrationContent(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = nameInput,
|
||||
onValueChange = handler.onNameInputChange,
|
||||
textFieldTestTag = "NameEntry",
|
||||
cardStyle = CardStyle.Top(dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
textFieldTestTag = "NameEntry",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(
|
||||
id = R.string.email_address,
|
||||
),
|
||||
label = stringResource(id = R.string.email_address),
|
||||
placeholder = stringResource(R.string.email_address_required),
|
||||
value = emailInput,
|
||||
onValueChange = handler.onEmailInputChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
keyboardType = KeyboardType.Email,
|
||||
textFieldTestTag = "EmailAddressEntry",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(2.dp))
|
||||
Row(
|
||||
supportingTextContent = {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
EnvironmentSelector(
|
||||
labelText = stringResource(id = R.string.creating_on),
|
||||
selectedOption = selectedEnvironmentType,
|
||||
onOptionSelected = handler.onEnvironmentTypeSelect,
|
||||
modifier = Modifier.testTag("RegionSelectorDropdown"),
|
||||
)
|
||||
if (isNewOnboardingUiEnabled) {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle_small,
|
||||
contentDescription = stringResource(
|
||||
R.string.help_with_server_geolocations,
|
||||
),
|
||||
onClick = handler.onServerGeologyHelpClick,
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
// Align with design but keep accessible touch target of IconButton.
|
||||
modifier = Modifier.offset(x = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
EnvironmentSelector(
|
||||
labelText = stringResource(id = R.string.creating_on),
|
||||
selectedOption = selectedEnvironmentType,
|
||||
onOptionSelected = handler.onEnvironmentTypeSelect,
|
||||
modifier = Modifier
|
||||
.testTag("RegionSelectorDropdown"),
|
||||
)
|
||||
if (isNewOnboardingUiEnabled) {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle_small,
|
||||
contentDescription = stringResource(R.string.help_with_server_geolocations),
|
||||
onClick = handler.onServerGeologyHelpClick,
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
// Align with design but keep accessible touch target of IconButton.
|
||||
modifier = Modifier.offset(y = (-8f).dp, x = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
if (selectedEnvironmentType != Environment.Type.SELF_HOSTED) {
|
||||
@@ -367,6 +369,7 @@ private fun ReceiveMarketingEmailsSwitch(
|
||||
isChecked = isChecked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
contentDescription = "ReceiveMarketingEmailsToggle",
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+18
-10
@@ -8,7 +8,6 @@ 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.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -18,11 +17,13 @@ import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.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.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@@ -30,12 +31,14 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.auth.feature.trusteddevice.handlers.TrustedDeviceHandlers
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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
|
||||
@@ -118,15 +121,16 @@ private fun TrustedDeviceScaffold(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.remember_this_device),
|
||||
description = stringResource(id = R.string.turn_off_using_public_device),
|
||||
supportingText = stringResource(id = R.string.turn_off_using_public_device),
|
||||
isChecked = state.isRemembered,
|
||||
onCheckedChange = handlers.onRememberToggle,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("RememberThisDeviceSwitch")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
@@ -136,7 +140,7 @@ private fun TrustedDeviceScaffold(
|
||||
label = stringResource(id = R.string.continue_text),
|
||||
onClick = handlers.onContinueClick,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@@ -148,7 +152,7 @@ private fun TrustedDeviceScaffold(
|
||||
onClick = handlers.onApproveWithDeviceClick,
|
||||
modifier = Modifier
|
||||
.testTag("ApproveWithOtherDeviceButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@@ -159,7 +163,7 @@ private fun TrustedDeviceScaffold(
|
||||
label = stringResource(id = R.string.request_admin_approval),
|
||||
onClick = handlers.onApproveWithAdminClick,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag("RequestAdminApprovalButton"),
|
||||
)
|
||||
@@ -172,7 +176,7 @@ private fun TrustedDeviceScaffold(
|
||||
onClick = handlers.onApproveWithPasswordClick,
|
||||
modifier = Modifier
|
||||
.testTag("ApproveWithMasterPasswordButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@@ -187,9 +191,10 @@ private fun TrustedDeviceScaffold(
|
||||
),
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.testTag("LoggingInAsLabel")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -198,9 +203,12 @@ private fun TrustedDeviceScaffold(
|
||||
onClick = handlers.onNotYouButtonClick,
|
||||
style = BitwardenTheme.typography.labelLarge,
|
||||
innerPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp),
|
||||
modifier = Modifier.testTag("NotYouLabel"),
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.testTag("NotYouLabel"),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
+16
-8
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -42,6 +43,7 @@ import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.description
|
||||
import com.x8bit.bitwarden.ui.auth.feature.twofactorlogin.util.title
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.LivecycleEventEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.OverflowMenuItemData
|
||||
@@ -50,6 +52,7 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -217,13 +220,14 @@ private fun TwoFactorLoginScreenContent(
|
||||
.imePadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = state.authMethod.description(state.displayEmail)(),
|
||||
textAlign = TextAlign.Start,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -255,31 +259,33 @@ private fun TwoFactorLoginScreenContent(
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = { onContinueButtonClick() },
|
||||
),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.remember_me),
|
||||
isChecked = state.isRememberMeEnabled,
|
||||
onCheckedChange = onRememberMeToggle,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
|
||||
BitwardenFilledButton(
|
||||
label = state.buttonText(),
|
||||
onClick = onContinueButtonClick,
|
||||
isEnabled = state.isContinueButtonEnabled,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -290,10 +296,12 @@ private fun TwoFactorLoginScreenContent(
|
||||
label = stringResource(id = R.string.send_verification_code_again),
|
||||
onClick = onResendEmailButtonClick,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +311,7 @@ private fun TwoFactorLoginScreenContentPreview() {
|
||||
BitwardenTheme {
|
||||
TwoFactorLoginScreenContent(
|
||||
state = TwoFactorLoginState(
|
||||
TwoFactorAuthMethod.EMAIL,
|
||||
authMethod = TwoFactorAuthMethod.EMAIL,
|
||||
availableAuthMethods = listOf(TwoFactorAuthMethod.EMAIL),
|
||||
codeInput = "",
|
||||
dialogState = null,
|
||||
|
||||
+28
-19
@@ -8,7 +8,6 @@ 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.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -47,6 +46,9 @@ import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.unlockScreenMessage
|
||||
import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.util.unlockScreenTitle
|
||||
import com.x8bit.bitwarden.ui.autofill.fido2.manager.Fido2CompletionManager
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.account.BitwardenAccountActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.account.BitwardenAccountSwitcher
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
@@ -58,6 +60,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLogoutConfirmationDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalBiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalFido2CompletionManager
|
||||
@@ -247,6 +250,7 @@ fun VaultUnlockScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
if (!state.hideInput) {
|
||||
BitwardenPasswordField(
|
||||
label = state.vaultUnlockType.unlockScreenInputLabel(),
|
||||
@@ -258,10 +262,6 @@ fun VaultUnlockScreen(
|
||||
showPasswordTestTag = state
|
||||
.vaultUnlockType
|
||||
.inputFieldVisibilityToggleTestTag,
|
||||
modifier = Modifier
|
||||
.testTag(state.vaultUnlockType.unlockScreenInputTestTag)
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
autoFocus = state.showKeyboard,
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardActions = KeyboardActions(
|
||||
@@ -269,17 +269,18 @@ fun VaultUnlockScreen(
|
||||
{ viewModel.trySendAction(VaultUnlockAction.UnlockClick) }
|
||||
},
|
||||
),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = state.vaultUnlockType.unlockScreenMessage(),
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
supportingText = state.vaultUnlockType.unlockScreenMessage(),
|
||||
cardStyle = CardStyle.Top(hasDivider = false),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.testTag(tag = state.vaultUnlockType.unlockScreenInputTestTag)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
val trailingTextCardStyle = if (state.hideInput) {
|
||||
CardStyle.Full
|
||||
} else {
|
||||
CardStyle.Bottom
|
||||
}
|
||||
Text(
|
||||
text = stringResource(
|
||||
@@ -287,11 +288,19 @@ fun VaultUnlockScreen(
|
||||
state.email,
|
||||
state.environmentUrl,
|
||||
),
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.testTag("UserAndEnvironmentDataLabel")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.cardBackground(cardStyle = trailingTextCardStyle)
|
||||
.cardPadding(
|
||||
cardStyle = trailingTextCardStyle,
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
top = 0.dp,
|
||||
bottom = 12.dp,
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
@@ -302,7 +311,7 @@ fun VaultUnlockScreen(
|
||||
{ viewModel.trySendAction(VaultUnlockAction.BiometricsUnlockClick) }
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@@ -312,7 +321,7 @@ fun VaultUnlockScreen(
|
||||
textAlign = TextAlign.Start,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.status.error,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
@@ -325,7 +334,7 @@ fun VaultUnlockScreen(
|
||||
isEnabled = state.input.isNotEmpty(),
|
||||
modifier = Modifier
|
||||
.testTag("UnlockVaultButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.x8bit.bitwarden.ui.platform.base.util
|
||||
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
|
||||
/**
|
||||
* Returns null if all entries in a given list are equal to a provided [value], otherwise
|
||||
* the original list is returned.
|
||||
@@ -10,3 +14,21 @@ fun <T> List<T>.nullIfAllEqual(value: T): List<T>? =
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate [CardStyle] based on the current [index] in the list.
|
||||
*/
|
||||
fun <T> Collection<T>.toListItemCardStyle(
|
||||
index: Int,
|
||||
hasDivider: Boolean = true,
|
||||
dividerPadding: Dp = 16.dp,
|
||||
): CardStyle =
|
||||
if (this.size == 1) {
|
||||
CardStyle.Full
|
||||
} else if (index == 0) {
|
||||
CardStyle.Top(hasDivider = hasDivider, dividerPadding = dividerPadding)
|
||||
} else if (index == this.size - 1) {
|
||||
CardStyle.Bottom
|
||||
} else {
|
||||
CardStyle.Middle(hasDivider = hasDivider, dividerPadding = dividerPadding)
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.x8bit.bitwarden.ui.platform.base.util
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
@@ -7,12 +9,14 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.CombinedModifier
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.focus.FocusDirection
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.isShiftPressed
|
||||
@@ -26,6 +30,7 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.platform.util.isPortrait
|
||||
|
||||
@@ -159,3 +164,72 @@ fun Modifier.standardHorizontalMargin(
|
||||
val config = LocalConfiguration.current
|
||||
return this.padding(horizontal = if (config.isPortrait) portrait else landscape)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a [Modifier] extension that applies a card background to the content.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
@Stable
|
||||
@Composable
|
||||
fun Modifier.cardBackground(
|
||||
cardStyle: CardStyle?,
|
||||
color: Color = BitwardenTheme.colorScheme.background.secondary,
|
||||
): Modifier {
|
||||
cardStyle ?: return this
|
||||
val shape = if ("robolectric" == Build.FINGERPRINT) {
|
||||
// TODO: This is here to ensure our click events work in robolectric tests because of the
|
||||
// uneven rounded corners that need to be clipped. This should be removed when the bug is
|
||||
// resolved: https://issuetracker.google.com/issues/366255137
|
||||
RectangleShape
|
||||
} else {
|
||||
when (cardStyle) {
|
||||
is CardStyle.Top -> BitwardenTheme.shapes.contentTop
|
||||
is CardStyle.Middle -> BitwardenTheme.shapes.contentMiddle
|
||||
CardStyle.Bottom -> BitwardenTheme.shapes.contentBottom
|
||||
CardStyle.Full -> BitwardenTheme.shapes.content
|
||||
}
|
||||
}
|
||||
return this
|
||||
.clip(shape = shape)
|
||||
.background(color = color, shape = shape)
|
||||
.bottomDivider(
|
||||
paddingStart = cardStyle.dividerPadding,
|
||||
enabled = cardStyle.hasDivider,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a [Modifier] extension that applies card padding to the content.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
@Stable
|
||||
@Composable
|
||||
fun Modifier.cardPadding(
|
||||
cardStyle: CardStyle?,
|
||||
start: Dp = 0.dp,
|
||||
top: Dp = 12.dp,
|
||||
end: Dp = 0.dp,
|
||||
bottom: Dp = 12.dp,
|
||||
): Modifier {
|
||||
cardStyle ?: return this
|
||||
return this.padding(start = start, top = top, end = end, bottom = bottom)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a [Modifier] extension that applies card padding to the content.
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
@Stable
|
||||
@Composable
|
||||
fun Modifier.cardPadding(
|
||||
cardStyle: CardStyle?,
|
||||
vertical: Dp = 12.dp,
|
||||
horizontal: Dp = 0.dp,
|
||||
): Modifier =
|
||||
this.cardPadding(
|
||||
cardStyle = cardStyle,
|
||||
start = horizontal,
|
||||
top = vertical,
|
||||
end = horizontal,
|
||||
bottom = vertical,
|
||||
)
|
||||
+1
-5
@@ -19,7 +19,6 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
@@ -74,10 +73,7 @@ fun BitwardenSearchTopAppBar(
|
||||
},
|
||||
title = {
|
||||
TextField(
|
||||
colors = bitwardenTextFieldColors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
),
|
||||
colors = bitwardenTextFieldColors(),
|
||||
textStyle = BitwardenTheme.typography.bodyLarge,
|
||||
placeholder = { Text(text = placeholder) },
|
||||
value = searchTerm,
|
||||
|
||||
+4
-1
@@ -5,6 +5,7 @@ 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.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.x8bit.bitwarden.R
|
||||
@@ -30,7 +31,9 @@ fun BitwardenFilledIconButton(
|
||||
isEnabled: Boolean = true,
|
||||
) {
|
||||
FilledIconButton(
|
||||
modifier = modifier.semantics(mergeDescendants = true) {},
|
||||
modifier = modifier.semantics(mergeDescendants = true) {
|
||||
this.contentDescription = contentDescription
|
||||
},
|
||||
onClick = onClick,
|
||||
colors = bitwardenFilledIconButtonColors(),
|
||||
enabled = isEnabled,
|
||||
|
||||
+4
-1
@@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.x8bit.bitwarden.R
|
||||
@@ -61,7 +62,9 @@ fun BitwardenStandardIconButton(
|
||||
contentColor: Color = BitwardenTheme.colorScheme.icon.primary,
|
||||
) {
|
||||
IconButton(
|
||||
modifier = modifier.semantics(mergeDescendants = true) {},
|
||||
modifier = modifier.semantics(mergeDescendants = true) {
|
||||
this.contentDescription = contentDescription
|
||||
},
|
||||
onClick = onClick,
|
||||
colors = bitwardenStandardIconButtonColors(contentColor = contentColor),
|
||||
enabled = isEnabled,
|
||||
|
||||
+4
-1
@@ -5,6 +5,7 @@ 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.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.x8bit.bitwarden.R
|
||||
@@ -30,7 +31,9 @@ fun BitwardenTonalIconButton(
|
||||
isEnabled: Boolean = true,
|
||||
) {
|
||||
FilledIconButton(
|
||||
modifier = modifier.semantics(mergeDescendants = true) {},
|
||||
modifier = modifier.semantics(mergeDescendants = true) {
|
||||
this.contentDescription = contentDescription
|
||||
},
|
||||
onClick = onClick,
|
||||
colors = bitwardenTonalIconButtonColors(),
|
||||
enabled = isEnabled,
|
||||
|
||||
+22
-4
@@ -2,6 +2,8 @@ package com.x8bit.bitwarden.ui.platform.components.dialog
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DatePickerColors
|
||||
import androidx.compose.material3.DatePickerDialog
|
||||
@@ -9,7 +11,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -27,10 +31,14 @@ import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldButtonColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.platform.util.orNow
|
||||
@@ -51,6 +59,7 @@ import java.time.ZonedDateTime
|
||||
* @param onDateSelect The callback to be invoked when a new date is selected.
|
||||
* @param isEnabled Whether the button is enabled.
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
|
||||
@@ -62,6 +71,7 @@ fun BitwardenDateSelectButton(
|
||||
onDateSelect: (ZonedDateTime) -> Unit,
|
||||
isEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
var shouldShowDialog: Boolean by rememberSaveable { mutableStateOf(false) }
|
||||
val formattedDate by remember(currentZonedDateTime) {
|
||||
@@ -72,18 +82,22 @@ fun BitwardenDateSelectButton(
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
TextField(
|
||||
modifier = modifier
|
||||
.clearAndSetSemantics {
|
||||
role = Role.DropdownList
|
||||
contentDescription = "$label, $formattedDate"
|
||||
}
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
enabled = isEnabled,
|
||||
indication = null,
|
||||
indication = ripple(color = BitwardenTheme.colorScheme.background.pressed),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { shouldShowDialog = !shouldShowDialog },
|
||||
),
|
||||
)
|
||||
.cardPadding(cardStyle = cardStyle)
|
||||
.padding(top = 4.dp),
|
||||
textStyle = BitwardenTheme.typography.bodyLarge,
|
||||
readOnly = true,
|
||||
label = { Text(text = label) },
|
||||
@@ -173,5 +187,9 @@ private fun bitwardenDatePickerColors(): DatePickerColors = DatePickerColors(
|
||||
dayInSelectionRangeContainerColor = BitwardenTheme.colorScheme.filledButton.background,
|
||||
dividerColor = BitwardenTheme.colorScheme.stroke.divider,
|
||||
dayInSelectionRangeContentColor = BitwardenTheme.colorScheme.text.primary,
|
||||
dateTextFieldColors = bitwardenTextFieldColors(),
|
||||
dateTextFieldColors = bitwardenTextFieldColors(
|
||||
disabledBorderColor = BitwardenTheme.colorScheme.outlineButton.borderDisabled,
|
||||
focusedBorderColor = BitwardenTheme.colorScheme.stroke.border,
|
||||
unfocusedBorderColor = BitwardenTheme.colorScheme.stroke.divider,
|
||||
),
|
||||
)
|
||||
+3
-1
@@ -22,6 +22,7 @@ import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -76,10 +77,11 @@ fun BitwardenMasterPasswordDialog(
|
||||
label = stringResource(id = R.string.master_password),
|
||||
value = masterPassword,
|
||||
onValueChange = { masterPassword = it },
|
||||
autoFocus = true,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("AlertInputField")
|
||||
.imePadding(),
|
||||
autoFocus = true,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
+5
-1
@@ -1,5 +1,6 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.dialog
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
@@ -18,6 +19,7 @@ import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -63,10 +65,12 @@ fun BitwardenPinDialog(
|
||||
label = stringResource(id = R.string.pin),
|
||||
value = pin,
|
||||
onValueChange = { pin = it },
|
||||
autoFocus = true,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("AlertInputField")
|
||||
.fillMaxWidth()
|
||||
.imePadding(),
|
||||
autoFocus = true,
|
||||
)
|
||||
},
|
||||
shape = BitwardenTheme.shapes.dialog,
|
||||
|
||||
+6
-4
@@ -1,5 +1,6 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.dialog
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -21,6 +22,7 @@ import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -77,12 +79,12 @@ fun BitwardenTextEntryDialog(
|
||||
label = textFieldLabel,
|
||||
value = text,
|
||||
onValueChange = { text = it },
|
||||
textFieldTestTag = "AlertContentText",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester)
|
||||
.onGloballyPositioned {
|
||||
shouldRequestFocus = true
|
||||
},
|
||||
textFieldTestTag = "AlertContentText",
|
||||
.onGloballyPositioned { shouldRequestFocus = true }
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
shape = BitwardenTheme.shapes.dialog,
|
||||
|
||||
+17
-3
@@ -2,9 +2,13 @@ package com.x8bit.bitwarden.ui.platform.components.dialog
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -16,8 +20,12 @@ import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldButtonColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.platform.util.orNow
|
||||
@@ -36,6 +44,7 @@ import java.time.ZonedDateTime
|
||||
* @param onTimeSelect The callback to be invoked when a new time is selected.
|
||||
* @param isEnabled Whether the button is enabled.
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param is24Hour Indicates if the time selector should use a 24 hour format or a 12 hour format
|
||||
* with AM/PM.
|
||||
*/
|
||||
@@ -47,6 +56,7 @@ fun BitwardenTimeSelectButton(
|
||||
onTimeSelect: (hour: Int, minute: Int) -> Unit,
|
||||
isEnabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
cardStyle: CardStyle? = null,
|
||||
is24Hour: Boolean = false,
|
||||
) {
|
||||
var shouldShowDialog: Boolean by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -57,18 +67,22 @@ fun BitwardenTimeSelectButton(
|
||||
?: "--:-- --",
|
||||
)
|
||||
}
|
||||
OutlinedTextField(
|
||||
TextField(
|
||||
modifier = modifier
|
||||
.clearAndSetSemantics {
|
||||
role = Role.DropdownList
|
||||
contentDescription = "$label, $formattedTime"
|
||||
}
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
enabled = isEnabled,
|
||||
indication = null,
|
||||
indication = ripple(color = BitwardenTheme.colorScheme.background.pressed),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { shouldShowDialog = !shouldShowDialog },
|
||||
),
|
||||
)
|
||||
.cardPadding(cardStyle = cardStyle)
|
||||
.padding(top = 4.dp),
|
||||
textStyle = BitwardenTheme.typography.bodyLarge,
|
||||
readOnly = true,
|
||||
label = { Text(text = label) },
|
||||
|
||||
+90
-42
@@ -2,14 +2,20 @@ package com.x8bit.bitwarden.ui.platform.components.dropdown
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -17,6 +23,7 @@ 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.semantics.CustomAccessibilityAction
|
||||
import androidx.compose.ui.semantics.Role
|
||||
@@ -29,11 +36,16 @@ 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.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldButtonColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TooltipData
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenRowOfActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -55,6 +67,12 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
* @param modifier A [Modifier] that you can use to apply custom modifications to the composable.
|
||||
* @param supportingText A optional supporting text that will appear below the text field.
|
||||
* @param tooltip A nullable [TooltipData], representing the tooltip icon.
|
||||
* @param insets Inner padding to be applied withing the card.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param actionsPadding Padding to be applied to the [actions] block.
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* in the app bar's trailing side. This lambda extends [RowScope], allowing flexibility in
|
||||
* defining the layout of the actions.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
@@ -67,10 +85,14 @@ fun BitwardenMultiSelectButton(
|
||||
isEnabled: Boolean = true,
|
||||
supportingText: String? = null,
|
||||
tooltip: TooltipData? = null,
|
||||
insets: PaddingValues = PaddingValues(),
|
||||
cardStyle: CardStyle? = null,
|
||||
actionsPadding: PaddingValues = PaddingValues(),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
OutlinedTextField(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clearAndSetSemantics {
|
||||
role = Role.DropdownList
|
||||
@@ -89,7 +111,7 @@ fun BitwardenMultiSelectButton(
|
||||
},
|
||||
)
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
indication = ripple(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
@@ -98,48 +120,74 @@ fun BitwardenMultiSelectButton(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
shouldShowDialog = !shouldShowDialog
|
||||
},
|
||||
textStyle = BitwardenTheme.typography.bodyLarge,
|
||||
readOnly = true,
|
||||
label = {
|
||||
Row {
|
||||
Text(
|
||||
text = label,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
tooltip?.let {
|
||||
Spacer(modifier = Modifier.width(3.dp))
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle_small,
|
||||
contentDescription = it.contentDescription,
|
||||
onClick = it.onClick,
|
||||
isEnabled = isEnabled,
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
modifier = Modifier.size(16.dp),
|
||||
}
|
||||
.cardPadding(cardStyle = cardStyle, vertical = 6.dp)
|
||||
.padding(paddingValues = insets),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TextField(
|
||||
textStyle = BitwardenTheme.typography.bodyLarge,
|
||||
readOnly = true,
|
||||
label = {
|
||||
Row {
|
||||
Text(
|
||||
text = label,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
tooltip?.let {
|
||||
Spacer(modifier = Modifier.width(3.dp))
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle_small,
|
||||
contentDescription = it.contentDescription,
|
||||
onClick = it.onClick,
|
||||
isEnabled = isEnabled,
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
value = selectedOption.orEmpty(),
|
||||
onValueChange = onOptionSelected,
|
||||
enabled = shouldShowDialog,
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = R.drawable.ic_chevron_down),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
value = selectedOption.orEmpty(),
|
||||
onValueChange = onOptionSelected,
|
||||
enabled = shouldShowDialog,
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = R.drawable.ic_chevron_down),
|
||||
contentDescription = null,
|
||||
},
|
||||
colors = bitwardenTextFieldButtonColors(),
|
||||
modifier = Modifier
|
||||
.weight(weight = 1f)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
colors = bitwardenTextFieldButtonColors(),
|
||||
supportingText = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = supportingText,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
BitwardenRowOfActions(
|
||||
modifier = Modifier.padding(paddingValues = actionsPadding),
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
supportingText?.let {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
}
|
||||
if (shouldShowDialog) {
|
||||
BitwardenSelectionDialog(
|
||||
title = label,
|
||||
|
||||
+1
-4
@@ -64,10 +64,7 @@ fun EnvironmentSelector(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { shouldShowDialog = !shouldShowDialog },
|
||||
)
|
||||
.padding(
|
||||
vertical = 8.dp,
|
||||
horizontal = 16.dp,
|
||||
),
|
||||
.padding(vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
|
||||
+8
-3
@@ -1,8 +1,8 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.field
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -10,8 +10,10 @@ import androidx.compose.ui.platform.LocalTextToolbar
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.toolbar.BitwardenEmptyTextToolbar
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -20,16 +22,18 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param label Label for the text field.
|
||||
* @param value Current text on the text field.
|
||||
* @param modifier Modifier for the composable.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenHiddenPasswordField(
|
||||
label: String,
|
||||
value: String,
|
||||
modifier: Modifier = Modifier,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
CompositionLocalProvider(value = LocalTextToolbar provides BitwardenEmptyTextToolbar) {
|
||||
OutlinedTextField(
|
||||
modifier = modifier,
|
||||
TextField(
|
||||
modifier = modifier.cardBackground(cardStyle = cardStyle),
|
||||
textStyle = BitwardenTheme.typography.sensitiveInfoSmall,
|
||||
label = { Text(text = label) },
|
||||
value = value,
|
||||
@@ -51,6 +55,7 @@ private fun BitwardenHiddenPasswordField_preview() {
|
||||
BitwardenHiddenPasswordField(
|
||||
label = "Label",
|
||||
value = "Password",
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
+256
-97
@@ -1,9 +1,16 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.field
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -28,12 +35,17 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
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.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.tabNavigation
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.toolbar.BitwardenCutCopyTextToolbar
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.toolbar.BitwardenEmptyTextToolbar
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TextToolbarType
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.nonLetterColorVisualTransformation
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
@@ -48,11 +60,12 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param showPassword Whether or not password should be shown.
|
||||
* @param showPasswordChange Lambda that is called when user request show/hide be toggled.
|
||||
* @param onValueChange Callback that is triggered when the password changes.
|
||||
* @param supportingTextContent An optional supporting text composable will appear below the text
|
||||
* input.
|
||||
* @param modifier Modifier for the composable.
|
||||
* @param readOnly `true` if the input should be read-only and not accept user interactions.
|
||||
* @param singleLine when `true`, this text field becomes a single line that horizontally scrolls
|
||||
* instead of wrapping onto multiple lines.
|
||||
* @param hint optional hint text that will appear below the text input.
|
||||
* @param showPasswordTestTag The test tag to be used on the show password button (testing tool).
|
||||
* @param autoFocus When set to true, the view will request focus after the first recomposition.
|
||||
* Setting this to true on multiple fields at once may have unexpected consequences.
|
||||
@@ -61,6 +74,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param imeAction the preferred IME action for the keyboard to have.
|
||||
* @param keyboardActions the callbacks of keyboard actions.
|
||||
* @param textToolbarType The type of [TextToolbar] to use on the text field.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
@@ -70,16 +84,17 @@ fun BitwardenPasswordField(
|
||||
showPassword: Boolean,
|
||||
showPasswordChange: (Boolean) -> Unit,
|
||||
onValueChange: (String) -> Unit,
|
||||
supportingTextContent: @Composable (ColumnScope.() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = true,
|
||||
hint: String? = null,
|
||||
showPasswordTestTag: String? = null,
|
||||
autoFocus: Boolean = false,
|
||||
keyboardType: KeyboardType = KeyboardType.Password,
|
||||
imeAction: ImeAction = ImeAction.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
textToolbarType: TextToolbarType = TextToolbarType.DEFAULT,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
var textFieldValueState by remember { mutableStateOf(TextFieldValue(text = value)) }
|
||||
@@ -103,63 +118,214 @@ fun BitwardenPasswordField(
|
||||
}
|
||||
var lastTextValue by remember(value) { mutableStateOf(value = value) }
|
||||
CompositionLocalProvider(value = LocalTextToolbar provides textToolbar) {
|
||||
OutlinedTextField(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, vertical = 6.dp)
|
||||
.tabNavigation()
|
||||
.focusRequester(focusRequester),
|
||||
colors = bitwardenTextFieldColors(),
|
||||
textStyle = BitwardenTheme.typography.sensitiveInfoSmall,
|
||||
label = { Text(text = label) },
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValueState = it
|
||||
val stringChangedSinceLastInvocation = lastTextValue != it.text
|
||||
lastTextValue = it.text
|
||||
if (stringChangedSinceLastInvocation) {
|
||||
onValueChange(it.text)
|
||||
}
|
||||
},
|
||||
visualTransformation = when {
|
||||
!showPassword -> PasswordVisualTransformation()
|
||||
readOnly -> nonLetterColorVisualTransformation()
|
||||
else -> VisualTransformation.None
|
||||
},
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction,
|
||||
),
|
||||
keyboardActions = keyboardActions,
|
||||
supportingText = hint?.let {
|
||||
{
|
||||
Text(
|
||||
text = hint,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
.focusRequester(focusRequester = focusRequester),
|
||||
) {
|
||||
TextField(
|
||||
colors = bitwardenTextFieldColors(),
|
||||
textStyle = BitwardenTheme.typography.sensitiveInfoSmall,
|
||||
label = { Text(text = label) },
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValueState = it
|
||||
val stringChangedSinceLastInvocation = lastTextValue != it.text
|
||||
lastTextValue = it.text
|
||||
if (stringChangedSinceLastInvocation) {
|
||||
onValueChange(it.text)
|
||||
}
|
||||
},
|
||||
visualTransformation = when {
|
||||
!showPassword -> PasswordVisualTransformation()
|
||||
readOnly -> nonLetterColorVisualTransformation()
|
||||
else -> VisualTransformation.None
|
||||
},
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction,
|
||||
),
|
||||
keyboardActions = keyboardActions,
|
||||
trailingIcon = {
|
||||
BitwardenStandardIconButton(
|
||||
modifier = Modifier.semantics { showPasswordTestTag?.let { testTag = it } },
|
||||
vectorIconRes = if (showPassword) {
|
||||
R.drawable.ic_eye_slash
|
||||
} else {
|
||||
R.drawable.ic_eye
|
||||
},
|
||||
contentDescription = stringResource(
|
||||
id = if (showPassword) R.string.hide else R.string.show,
|
||||
),
|
||||
onClick = { showPasswordChange.invoke(!showPassword) },
|
||||
)
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
BitwardenStandardIconButton(
|
||||
modifier = Modifier.semantics { showPasswordTestTag?.let { testTag = it } },
|
||||
vectorIconRes = if (showPassword) {
|
||||
R.drawable.ic_eye_slash
|
||||
} else {
|
||||
R.drawable.ic_eye
|
||||
},
|
||||
contentDescription = stringResource(
|
||||
id = if (showPassword) R.string.hide else R.string.show,
|
||||
),
|
||||
onClick = { showPasswordChange.invoke(!showPassword) },
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
supportingTextContent?.let {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
content = it,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 6.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (autoFocus) {
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Bitwarden-styled password field that hoists show/hide password state to the caller.
|
||||
*
|
||||
* See overloaded [BitwardenPasswordField] for self managed show/hide state.
|
||||
*
|
||||
* @param label Label for the text field.
|
||||
* @param value Current next on the text field.
|
||||
* @param showPassword Whether or not password should be shown.
|
||||
* @param showPasswordChange Lambda that is called when user request show/hide be toggled.
|
||||
* @param onValueChange Callback that is triggered when the password changes.
|
||||
* @param modifier Modifier for the composable.
|
||||
* @param readOnly `true` if the input should be read-only and not accept user interactions.
|
||||
* @param singleLine when `true`, this text field becomes a single line that horizontally scrolls
|
||||
* instead of wrapping onto multiple lines.
|
||||
* @param supportingText An optional supporting text that will appear below the text input.
|
||||
* @param showPasswordTestTag The test tag to be used on the show password button (testing tool).
|
||||
* @param autoFocus When set to true, the view will request focus after the first recomposition.
|
||||
* Setting this to true on multiple fields at once may have unexpected consequences.
|
||||
* @param keyboardType The type of keyboard the user has access to when inputting values into
|
||||
* the password field.
|
||||
* @param imeAction the preferred IME action for the keyboard to have.
|
||||
* @param keyboardActions the callbacks of keyboard actions.
|
||||
* @param textToolbarType The type of [TextToolbar] to use on the text field.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenPasswordField(
|
||||
label: String,
|
||||
value: String,
|
||||
showPassword: Boolean,
|
||||
showPasswordChange: (Boolean) -> Unit,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = true,
|
||||
supportingText: String? = null,
|
||||
showPasswordTestTag: String? = null,
|
||||
autoFocus: Boolean = false,
|
||||
keyboardType: KeyboardType = KeyboardType.Password,
|
||||
imeAction: ImeAction = ImeAction.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
textToolbarType: TextToolbarType = TextToolbarType.DEFAULT,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
BitwardenPasswordField(
|
||||
label = label,
|
||||
value = value,
|
||||
showPassword = showPassword,
|
||||
showPasswordChange = showPasswordChange,
|
||||
showPasswordTestTag = showPasswordTestTag,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
supportingTextContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
},
|
||||
autoFocus = autoFocus,
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction,
|
||||
keyboardActions = keyboardActions,
|
||||
textToolbarType = textToolbarType,
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Bitwarden-styled password field that hoists show/hide password state to the caller.
|
||||
*
|
||||
* See overloaded [BitwardenPasswordField] for self managed show/hide state.
|
||||
*
|
||||
* @param label Label for the text field.
|
||||
* @param value Current next on the text field.
|
||||
* @param onValueChange Callback that is triggered when the password changes.
|
||||
* @param modifier Modifier for the composable.
|
||||
* @param initialShowPassword The initial state of the show/hide password control. A value of
|
||||
* `false` (the default) indicates that that password should begin in the hidden state.
|
||||
* @param readOnly `true` if the input should be read-only and not accept user interactions.
|
||||
* @param singleLine when `true`, this text field becomes a single line that horizontally scrolls
|
||||
* instead of wrapping onto multiple lines.
|
||||
* @param supportingTextContent An optional supporting text composable will appear below the text
|
||||
* input.
|
||||
* @param showPasswordTestTag The test tag to be used on the show password button (testing tool).
|
||||
* @param autoFocus When set to true, the view will request focus after the first recomposition.
|
||||
* Setting this to true on multiple fields at once may have unexpected consequences.
|
||||
* @param keyboardType The type of keyboard the user has access to when inputting values into
|
||||
* the password field.
|
||||
* @param imeAction the preferred IME action for the keyboard to have.
|
||||
* @param keyboardActions the callbacks of keyboard actions.
|
||||
* @param textToolbarType The type of [TextToolbar] to use on the text field.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenPasswordField(
|
||||
label: String,
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
initialShowPassword: Boolean = false,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = true,
|
||||
supportingTextContent: @Composable (ColumnScope.() -> Unit)?,
|
||||
showPasswordTestTag: String? = null,
|
||||
autoFocus: Boolean = false,
|
||||
keyboardType: KeyboardType = KeyboardType.Password,
|
||||
imeAction: ImeAction = ImeAction.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
textToolbarType: TextToolbarType = TextToolbarType.DEFAULT,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
var showPassword by rememberSaveable { mutableStateOf(value = initialShowPassword) }
|
||||
BitwardenPasswordField(
|
||||
label = label,
|
||||
value = value,
|
||||
showPassword = showPassword,
|
||||
showPasswordChange = { showPassword = !showPassword },
|
||||
showPasswordTestTag = showPasswordTestTag,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
supportingTextContent = supportingTextContent,
|
||||
autoFocus = autoFocus,
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction,
|
||||
keyboardActions = keyboardActions,
|
||||
textToolbarType = textToolbarType,
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Bitwarden-styled password field that manages the state of a show/hide indicator
|
||||
* internally.
|
||||
@@ -171,7 +337,7 @@ fun BitwardenPasswordField(
|
||||
* @param readOnly `true` if the input should be read-only and not accept user interactions.
|
||||
* @param singleLine when `true`, this text field becomes a single line that horizontally scrolls
|
||||
* instead of wrapping onto multiple lines.
|
||||
* @param hint optional hint text that will appear below the text input.
|
||||
* @param supportingText An optional supporting text that will appear below the text input.
|
||||
* @param initialShowPassword The initial state of the show/hide password control. A value of
|
||||
* `false` (the default) indicates that that password should begin in the hidden state.
|
||||
* @param showPasswordTestTag The test tag to be used on the show password button (testing tool).
|
||||
@@ -181,6 +347,7 @@ fun BitwardenPasswordField(
|
||||
* the password field.
|
||||
* @param imeAction the preferred IME action for the keyboard to have.
|
||||
* @param keyboardActions the callbacks of keyboard actions.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenPasswordField(
|
||||
@@ -190,13 +357,14 @@ fun BitwardenPasswordField(
|
||||
modifier: Modifier = Modifier,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = true,
|
||||
hint: String? = null,
|
||||
supportingText: String? = null,
|
||||
initialShowPassword: Boolean = false,
|
||||
showPasswordTestTag: String? = null,
|
||||
autoFocus: Boolean = false,
|
||||
keyboardType: KeyboardType = KeyboardType.Password,
|
||||
imeAction: ImeAction = ImeAction.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
var showPassword by rememberSaveable { mutableStateOf(initialShowPassword) }
|
||||
BitwardenPasswordField(
|
||||
@@ -208,59 +376,50 @@ fun BitwardenPasswordField(
|
||||
onValueChange = onValueChange,
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
hint = hint,
|
||||
supportingText = supportingText,
|
||||
showPasswordTestTag = showPasswordTestTag,
|
||||
autoFocus = autoFocus,
|
||||
keyboardType = keyboardType,
|
||||
imeAction = imeAction,
|
||||
keyboardActions = keyboardActions,
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenPasswordField_preview_withInput_hidePassword() {
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "Password",
|
||||
onValueChange = {},
|
||||
initialShowPassword = false,
|
||||
hint = "Hint",
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenPasswordField_preview_withInput_showPassword() {
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "Password",
|
||||
onValueChange = {},
|
||||
initialShowPassword = true,
|
||||
hint = "Hint",
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenPasswordField_preview_withoutInput_hidePassword() {
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
initialShowPassword = false,
|
||||
hint = "Hint",
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenPasswordField_preview_withoutInput_showPassword() {
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
initialShowPassword = true,
|
||||
hint = "Hint",
|
||||
)
|
||||
private fun BitwardenPasswordField_preview() {
|
||||
BitwardenTheme {
|
||||
Column {
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "Password",
|
||||
onValueChange = {},
|
||||
initialShowPassword = false,
|
||||
cardStyle = CardStyle.Top(),
|
||||
)
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "Password",
|
||||
onValueChange = {},
|
||||
initialShowPassword = true,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
)
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
initialShowPassword = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
)
|
||||
BitwardenPasswordField(
|
||||
label = "Label",
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
initialShowPassword = true,
|
||||
supportingText = "Hint",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
-22
@@ -1,8 +1,16 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.field
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -16,8 +24,13 @@ import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
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.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TextToolbarType
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenRowOfActions
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -32,10 +45,14 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param showPasswordChange Lambda that is called when user request show/hide be toggled.
|
||||
* @param onValueChange Callback that is triggered when the password changes.
|
||||
* @param modifier Modifier for the composable.
|
||||
* @param supportingText An optional supporting text that will appear below the text field and
|
||||
* actions.
|
||||
* @param readOnly `true` if the input should be read-only and not accept user interactions.
|
||||
* @param singleLine when `true`, this text field becomes a single line that horizontally scrolls
|
||||
* instead of wrapping onto multiple lines.
|
||||
* @param textToolbarType The type of [TextToolbar] to use on the text field.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param actionsPadding Padding to be applied to the [actions] block.
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* in the app bar's trailing side. This lambda extends [RowScope], allowing flexibility in
|
||||
* defining the layout of the actions.
|
||||
@@ -48,33 +65,62 @@ fun BitwardenPasswordFieldWithActions(
|
||||
showPasswordChange: (Boolean) -> Unit,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingText: String? = null,
|
||||
readOnly: Boolean = false,
|
||||
singleLine: Boolean = false,
|
||||
showPasswordTestTag: String? = null,
|
||||
passwordFieldTestTag: String? = null,
|
||||
textToolbarType: TextToolbarType = TextToolbarType.DEFAULT,
|
||||
cardStyle: CardStyle? = null,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier.semantics(mergeDescendants = true) {},
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, vertical = 6.dp)
|
||||
.semantics(mergeDescendants = true) { },
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
BitwardenPasswordField(
|
||||
label = label,
|
||||
value = value,
|
||||
showPasswordChange = showPasswordChange,
|
||||
showPassword = showPassword,
|
||||
onValueChange = onValueChange,
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
modifier = Modifier
|
||||
.semantics { passwordFieldTestTag?.let { testTag = it } }
|
||||
.weight(1f)
|
||||
.padding(end = 8.dp),
|
||||
showPasswordTestTag = showPasswordTestTag,
|
||||
textToolbarType = textToolbarType,
|
||||
)
|
||||
actions()
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
BitwardenPasswordField(
|
||||
label = label,
|
||||
value = value,
|
||||
showPasswordChange = showPasswordChange,
|
||||
showPassword = showPassword,
|
||||
onValueChange = onValueChange,
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
showPasswordTestTag = showPasswordTestTag,
|
||||
textToolbarType = textToolbarType,
|
||||
modifier = Modifier
|
||||
.semantics { passwordFieldTestTag?.let { testTag = it } }
|
||||
.weight(weight = 1f),
|
||||
)
|
||||
BitwardenRowOfActions(
|
||||
modifier = Modifier.padding(paddingValues = actionsPadding),
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
supportingText?.let {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +140,11 @@ fun BitwardenPasswordFieldWithActions(
|
||||
* @param initialShowPassword The initial state of the show/hide password control. A value of
|
||||
* `false` (the default) indicates that that password should begin in the hidden state.
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* in the app bar's trailing side. This lambda extends [RowScope], allowing flexibility in
|
||||
* defining the layout of the actions.
|
||||
* @param actionsPadding Padding to be applied to the [actions] block.
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* in the app bar's trailing side. This lambda extends [RowScope], allowing flexibility in
|
||||
* defining the layout of the actions.
|
||||
*/
|
||||
@@ -108,6 +159,8 @@ fun BitwardenPasswordFieldWithActions(
|
||||
initialShowPassword: Boolean = false,
|
||||
showPasswordTestTag: String? = null,
|
||||
passwordFieldTestTag: String? = null,
|
||||
cardStyle: CardStyle? = null,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
var shouldShowPassword by remember { mutableStateOf(initialShowPassword) }
|
||||
@@ -122,11 +175,12 @@ fun BitwardenPasswordFieldWithActions(
|
||||
singleLine = singleLine,
|
||||
showPasswordTestTag = showPasswordTestTag,
|
||||
passwordFieldTestTag = passwordFieldTestTag,
|
||||
actionsPadding = actionsPadding,
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenPasswordFieldWithActions_preview() {
|
||||
BitwardenTheme {
|
||||
@@ -135,12 +189,13 @@ private fun BitwardenPasswordFieldWithActions_preview() {
|
||||
value = "samplePassword",
|
||||
onValueChange = {},
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_check_mark,
|
||||
contentDescription = "",
|
||||
onClick = {},
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
+177
-69
@@ -1,13 +1,19 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.field
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -33,12 +39,16 @@ import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toPx
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.withLineBreaksAtWidth
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.color.bitwardenMenuItemColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.toolbar.BitwardenCutCopyTextToolbar
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.toolbar.BitwardenEmptyTextToolbar
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TextToolbarType
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
@@ -57,7 +67,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
* the [value] is empty.
|
||||
* @param leadingIconResource the optional resource for the leading icon on the text field.
|
||||
* @param trailingIconContent the content for the trailing icon in the text field.
|
||||
* @param hint optional hint text that will appear below the text input.
|
||||
* @param supportingText optional supporting text that will appear below the text input.
|
||||
* @param singleLine when `true`, this text field becomes a single line that horizontally scrolls
|
||||
* instead of wrapping onto multiple lines.
|
||||
* @param readOnly `true` if the input should be read-only and not accept user interactions.
|
||||
@@ -68,8 +78,8 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
* @param visualTransformation Transforms the visual representation of the input [value].
|
||||
* @param keyboardType the preferred type of keyboard input.
|
||||
* @param textToolbarType The type of [TextToolbar] to use on the text field.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun BitwardenTextField(
|
||||
label: String?,
|
||||
@@ -79,7 +89,7 @@ fun BitwardenTextField(
|
||||
placeholder: String? = null,
|
||||
leadingIconResource: IconResource? = null,
|
||||
trailingIconContent: (@Composable () -> Unit)? = null,
|
||||
hint: String? = null,
|
||||
supportingText: String? = null,
|
||||
singleLine: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
@@ -92,6 +102,91 @@ fun BitwardenTextField(
|
||||
textToolbarType: TextToolbarType = TextToolbarType.DEFAULT,
|
||||
autoCompleteOptions: ImmutableList<String> = persistentListOf(),
|
||||
textFieldTestTag: String? = null,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
BitwardenTextField(
|
||||
modifier = modifier,
|
||||
label = label,
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
placeholder = placeholder,
|
||||
leadingIconResource = leadingIconResource,
|
||||
trailingIconContent = trailingIconContent,
|
||||
supportingTextContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
},
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
enabled = enabled,
|
||||
textStyle = textStyle,
|
||||
shouldAddCustomLineBreaks = shouldAddCustomLineBreaks,
|
||||
keyboardType = keyboardType,
|
||||
isError = isError,
|
||||
autoFocus = autoFocus,
|
||||
visualTransformation = visualTransformation,
|
||||
textToolbarType = textToolbarType,
|
||||
autoCompleteOptions = autoCompleteOptions,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that allows the user to input text. This composable will manage the state of
|
||||
* the user's input.
|
||||
* @param label label for the text field.
|
||||
* @param value current next on the text field.
|
||||
* @param modifier modifier for the composable.
|
||||
* @param onValueChange callback that is triggered when the input of the text field changes.
|
||||
* @param supportingTextContent An optional supporting text composable that will appear below the
|
||||
* text input.
|
||||
* @param placeholder the optional placeholder to be displayed when the text field is in focus and
|
||||
* the [value] is empty.
|
||||
* @param leadingIconResource the optional resource for the leading icon on the text field.
|
||||
* @param trailingIconContent the content for the trailing icon in the text field.
|
||||
* @param singleLine when `true`, this text field becomes a single line that horizontally scrolls
|
||||
* instead of wrapping onto multiple lines.
|
||||
* @param readOnly `true` if the input should be read-only and not accept user interactions.
|
||||
* @param enabled Whether or not the text field is enabled.
|
||||
* @param textStyle An optional style that may be used to override the default used.
|
||||
* @param shouldAddCustomLineBreaks If `true`, line breaks will be inserted to allow for filling
|
||||
* an entire line before breaking. `false` by default.
|
||||
* @param visualTransformation Transforms the visual representation of the input [value].
|
||||
* @param keyboardType the preferred type of keyboard input.
|
||||
* @param textToolbarType The type of [TextToolbar] to use on the text field.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun BitwardenTextField(
|
||||
label: String?,
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
supportingTextContent: (@Composable ColumnScope.() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
placeholder: String? = null,
|
||||
leadingIconResource: IconResource? = null,
|
||||
trailingIconContent: (@Composable () -> Unit)? = null,
|
||||
singleLine: Boolean = true,
|
||||
readOnly: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
textStyle: TextStyle = BitwardenTheme.typography.bodyLarge,
|
||||
shouldAddCustomLineBreaks: Boolean = false,
|
||||
keyboardType: KeyboardType = KeyboardType.Text,
|
||||
isError: Boolean = false,
|
||||
autoFocus: Boolean = false,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
textToolbarType: TextToolbarType = TextToolbarType.DEFAULT,
|
||||
autoCompleteOptions: ImmutableList<String> = persistentListOf(),
|
||||
textFieldTestTag: String? = null,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
var widthPx by remember { mutableIntStateOf(0) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@@ -126,59 +221,71 @@ fun BitwardenTextField(
|
||||
var lastTextValue by remember(value) { mutableStateOf(value = value) }
|
||||
CompositionLocalProvider(value = LocalTextToolbar provides textToolbar) {
|
||||
var hasFocused by remember { mutableStateOf(value = false) }
|
||||
Box(modifier = modifier) {
|
||||
OutlinedTextField(
|
||||
colors = bitwardenTextFieldColors(),
|
||||
Box(modifier = modifier.defaultMinSize(minHeight = 60.dp)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.testTag(textFieldTestTag.orEmpty())
|
||||
.onGloballyPositioned { widthPx = it.size.width }
|
||||
.onFocusEvent { focusState -> hasFocused = focusState.hasFocus }
|
||||
.focusRequester(focusRequester)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, vertical = 6.dp)
|
||||
.fillMaxWidth(),
|
||||
enabled = enabled,
|
||||
label = label?.let { { Text(text = it) } },
|
||||
value = textFieldValue,
|
||||
leadingIcon = leadingIconResource?.let { iconResource ->
|
||||
{
|
||||
Icon(
|
||||
painter = iconResource.iconPainter,
|
||||
contentDescription = iconResource.contentDescription,
|
||||
)
|
||||
}
|
||||
},
|
||||
trailingIcon = trailingIconContent,
|
||||
placeholder = placeholder?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = textStyle,
|
||||
)
|
||||
}
|
||||
},
|
||||
supportingText = hint?.let {
|
||||
{
|
||||
Text(
|
||||
text = hint,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
},
|
||||
onValueChange = {
|
||||
hasFocused = true
|
||||
textFieldValueState = it
|
||||
val stringChangedSinceLastInvocation = lastTextValue != it.text
|
||||
lastTextValue = it.text
|
||||
if (stringChangedSinceLastInvocation) {
|
||||
onValueChange(it.text)
|
||||
}
|
||||
},
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
textStyle = textStyle,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = keyboardType),
|
||||
isError = isError,
|
||||
visualTransformation = visualTransformation,
|
||||
)
|
||||
) {
|
||||
TextField(
|
||||
colors = bitwardenTextFieldColors(),
|
||||
enabled = enabled,
|
||||
label = label?.let { { Text(text = it) } },
|
||||
value = textFieldValue,
|
||||
leadingIcon = leadingIconResource?.let { iconResource ->
|
||||
{
|
||||
Icon(
|
||||
painter = iconResource.iconPainter,
|
||||
contentDescription = iconResource.contentDescription,
|
||||
)
|
||||
}
|
||||
},
|
||||
trailingIcon = trailingIconContent,
|
||||
placeholder = placeholder?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = textStyle,
|
||||
)
|
||||
}
|
||||
},
|
||||
onValueChange = {
|
||||
hasFocused = true
|
||||
textFieldValueState = it
|
||||
val stringChangedSinceLastInvocation = lastTextValue != it.text
|
||||
lastTextValue = it.text
|
||||
if (stringChangedSinceLastInvocation) {
|
||||
onValueChange(it.text)
|
||||
}
|
||||
},
|
||||
singleLine = singleLine,
|
||||
readOnly = readOnly,
|
||||
textStyle = textStyle,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = keyboardType),
|
||||
isError = isError,
|
||||
visualTransformation = visualTransformation,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
supportingTextContent?.let {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
content = it,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 6.dp))
|
||||
}
|
||||
}
|
||||
val filteredAutoCompleteList = autoCompleteOptions
|
||||
.filter { option ->
|
||||
option.startsWith(textFieldValue.text) && option != textFieldValue.text
|
||||
@@ -208,22 +315,23 @@ fun BitwardenTextField(
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenTextField_preview_withInput() {
|
||||
BitwardenTextField(
|
||||
label = "Label",
|
||||
value = "Input",
|
||||
onValueChange = {},
|
||||
hint = "Hint",
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenTextField_preview_withoutInput() {
|
||||
BitwardenTextField(
|
||||
label = "Label",
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
hint = "Hint",
|
||||
)
|
||||
private fun BitwardenTextField_preview() {
|
||||
BitwardenTheme {
|
||||
Column {
|
||||
BitwardenTextField(
|
||||
label = "Label",
|
||||
value = "Input",
|
||||
onValueChange = {},
|
||||
supportingText = "Hint",
|
||||
cardStyle = CardStyle.Top(),
|
||||
)
|
||||
BitwardenTextField(
|
||||
label = "Label",
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
supportingText = "Hint",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-23
@@ -1,9 +1,17 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.field
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -14,7 +22,12 @@ import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
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.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TextToolbarType
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenRowOfActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -29,6 +42,8 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param value Current text in the text field.
|
||||
* @param onValueChange Callback that is triggered when the text content changes.
|
||||
* @param modifier [Modifier] applied to this layout composable.
|
||||
* @param supportingText An optional supporting text that will appear below the text field and
|
||||
* actions.
|
||||
* @param textStyle The [TextStyle], or null if default.
|
||||
* @param shouldAddCustomLineBreaks If `true`, line breaks will be inserted to allow for filling
|
||||
* an entire line before breaking. `false` by default.
|
||||
@@ -41,9 +56,11 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* next to the text field. This lambda extends [RowScope],
|
||||
* providing flexibility in the layout definition.
|
||||
* @param actionsPadding Padding to be applied to the [actions] block.
|
||||
* @param actionsTestTag The test tag to use for the row of actions, or null if there is none.
|
||||
* @param textFieldTestTag The test tag to be used on the text field.
|
||||
* @param textToolbarType The type of [TextToolbar] to use on the text field.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenTextFieldWithActions(
|
||||
@@ -51,6 +68,7 @@ fun BitwardenTextFieldWithActions(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingText: String? = null,
|
||||
textStyle: TextStyle = BitwardenTheme.typography.bodyLarge,
|
||||
shouldAddCustomLineBreaks: Boolean = false,
|
||||
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||
@@ -59,39 +77,65 @@ fun BitwardenTextFieldWithActions(
|
||||
keyboardType: KeyboardType = KeyboardType.Text,
|
||||
trailingIconContent: (@Composable () -> Unit)? = null,
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
actionsTestTag: String? = null,
|
||||
textFieldTestTag: String? = null,
|
||||
textToolbarType: TextToolbarType = TextToolbarType.DEFAULT,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
Row(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, vertical = 6.dp)
|
||||
.semantics(mergeDescendants = true) { },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
BitwardenTextField(
|
||||
modifier = Modifier.weight(1f),
|
||||
label = label,
|
||||
value = value,
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
onValueChange = onValueChange,
|
||||
keyboardType = keyboardType,
|
||||
trailingIconContent = trailingIconContent,
|
||||
textStyle = textStyle,
|
||||
shouldAddCustomLineBreaks = shouldAddCustomLineBreaks,
|
||||
visualTransformation = visualTransformation,
|
||||
textToolbarType = textToolbarType,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
)
|
||||
BitwardenRowOfActions(
|
||||
modifier = Modifier.run { actionsTestTag?.let { testTag(it) } ?: this },
|
||||
actions = actions,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
BitwardenTextField(
|
||||
modifier = Modifier.weight(weight = 1f),
|
||||
label = label,
|
||||
value = value,
|
||||
readOnly = readOnly,
|
||||
singleLine = singleLine,
|
||||
onValueChange = onValueChange,
|
||||
keyboardType = keyboardType,
|
||||
trailingIconContent = trailingIconContent,
|
||||
textStyle = textStyle,
|
||||
shouldAddCustomLineBreaks = shouldAddCustomLineBreaks,
|
||||
visualTransformation = visualTransformation,
|
||||
textToolbarType = textToolbarType,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
)
|
||||
BitwardenRowOfActions(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = actionsPadding)
|
||||
.run { actionsTestTag?.let { testTag(it) } ?: this },
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
supportingText?.let {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenTextFieldWithActions_preview() {
|
||||
BitwardenTheme {
|
||||
@@ -99,6 +143,7 @@ private fun BitwardenTextFieldWithActions_preview() {
|
||||
label = "Username",
|
||||
value = "user@example.com",
|
||||
onValueChange = {},
|
||||
supportingText = "I'm here for support",
|
||||
actions = {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = R.drawable.ic_question_circle),
|
||||
@@ -109,6 +154,7 @@ private fun BitwardenTextFieldWithActions_preview() {
|
||||
contentDescription = "Action 2",
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -11,10 +11,10 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
*/
|
||||
@Composable
|
||||
fun bitwardenTextFieldButtonColors(): TextFieldColors = bitwardenTextFieldColors(
|
||||
unfocusedBorderColor = BitwardenTheme.colorScheme.stroke.divider,
|
||||
focusedBorderColor = BitwardenTheme.colorScheme.stroke.divider,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
focusedBorderColor = Color.Transparent,
|
||||
disabledTextColor = BitwardenTheme.colorScheme.text.primary,
|
||||
disabledBorderColor = BitwardenTheme.colorScheme.stroke.divider,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
disabledLeadingIconColor = BitwardenTheme.colorScheme.icon.primary,
|
||||
disabledTrailingIconColor = BitwardenTheme.colorScheme.icon.primary,
|
||||
disabledLabelColor = BitwardenTheme.colorScheme.text.secondary,
|
||||
@@ -27,10 +27,10 @@ fun bitwardenTextFieldButtonColors(): TextFieldColors = bitwardenTextFieldColors
|
||||
*/
|
||||
@Composable
|
||||
fun bitwardenTextFieldColors(
|
||||
unfocusedBorderColor: Color = BitwardenTheme.colorScheme.stroke.divider,
|
||||
focusedBorderColor: Color = BitwardenTheme.colorScheme.stroke.border,
|
||||
focusedBorderColor: Color = Color.Transparent,
|
||||
unfocusedBorderColor: Color = Color.Transparent,
|
||||
disabledTextColor: Color = BitwardenTheme.colorScheme.outlineButton.foregroundDisabled,
|
||||
disabledBorderColor: Color = BitwardenTheme.colorScheme.outlineButton.borderDisabled,
|
||||
disabledBorderColor: Color = Color.Transparent,
|
||||
disabledLeadingIconColor: Color = BitwardenTheme.colorScheme.outlineButton.foregroundDisabled,
|
||||
disabledTrailingIconColor: Color = BitwardenTheme.colorScheme.outlineButton.foregroundDisabled,
|
||||
disabledLabelColor: Color = BitwardenTheme.colorScheme.outlineButton.foregroundDisabled,
|
||||
|
||||
+1
-6
@@ -1,12 +1,10 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.header
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -27,10 +25,7 @@ fun BitwardenListHeaderText(
|
||||
text = "${label.uppercase()}$supportLabel",
|
||||
style = BitwardenTheme.typography.eyebrowMedium,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = modifier.padding(
|
||||
top = 12.dp,
|
||||
bottom = 4.dp,
|
||||
),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+13
-24
@@ -4,7 +4,7 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
@@ -19,8 +19,9 @@ import androidx.compose.ui.semantics.testTag
|
||||
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.bottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.mirrorIfRtl
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -35,6 +36,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param modifier The [Modifier] to be applied to the [Row] composable that holds the list item.
|
||||
* @param showDivider Indicates whether the divider should be shown or not.
|
||||
* @param startIconTestTag The optional test tag for the [startIcon].
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenGroupItem(
|
||||
@@ -45,9 +47,12 @@ fun BitwardenGroupItem(
|
||||
modifier: Modifier = Modifier,
|
||||
showDivider: Boolean = true,
|
||||
startIconTestTag: String? = null,
|
||||
cardStyle: CardStyle?,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
@@ -55,16 +60,7 @@ fun BitwardenGroupItem(
|
||||
),
|
||||
onClick = onClick,
|
||||
)
|
||||
.bottomDivider(
|
||||
enabled = showDivider,
|
||||
paddingStart = 16.dp,
|
||||
)
|
||||
.padding(
|
||||
top = 16.dp,
|
||||
bottom = 16.dp,
|
||||
end = 8.dp,
|
||||
)
|
||||
.then(modifier),
|
||||
.cardPadding(cardStyle = cardStyle, horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
@@ -73,6 +69,7 @@ fun BitwardenGroupItem(
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.primary,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 36.dp)
|
||||
.semantics { startIconTestTag?.let { testTag = it } }
|
||||
.size(24.dp),
|
||||
)
|
||||
@@ -89,19 +86,10 @@ fun BitwardenGroupItem(
|
||||
style = BitwardenTheme.typography.labelSmall,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
)
|
||||
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = R.drawable.ic_chevron_right),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.primary,
|
||||
modifier = Modifier
|
||||
.mirrorIfRtl()
|
||||
.size(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BitwardenGroupItem_preview() {
|
||||
BitwardenTheme {
|
||||
@@ -111,6 +99,7 @@ private fun BitwardenGroupItem_preview() {
|
||||
startIcon = rememberVectorPainter(id = R.drawable.ic_file_text),
|
||||
startIconTestTag = "Test Tag",
|
||||
onClick = {},
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
-7
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -30,10 +29,14 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
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.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenBasicDialogRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.icon.BitwardenIcon
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
@@ -59,6 +62,7 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
* @param supportingLabelTestTag The optional test tag for the [supportingLabel].
|
||||
* @param startIconTestTag The optional test tag for the [startIcon].
|
||||
* @param trailingLabelIcons An optional list of small icons to be displayed after the [label].
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
@@ -76,10 +80,13 @@ fun BitwardenListItem(
|
||||
supportingLabelTestTag: String? = null,
|
||||
startIconTestTag: String? = null,
|
||||
trailingLabelIcons: ImmutableList<IconResource> = persistentListOf(),
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||
Row(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
@@ -87,9 +94,11 @@ fun BitwardenListItem(
|
||||
),
|
||||
onClick = onClick,
|
||||
)
|
||||
.defaultMinSize(minHeight = 72.dp)
|
||||
.padding(vertical = 8.dp)
|
||||
.then(modifier),
|
||||
.cardPadding(
|
||||
cardStyle = cardStyle,
|
||||
start = 16.dp,
|
||||
end = 4.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
@@ -130,7 +139,7 @@ fun BitwardenListItem(
|
||||
}
|
||||
}
|
||||
|
||||
secondSupportingLabel?.let { secondSupportLabel ->
|
||||
secondSupportingLabel.orNullIfBlank()?.let { secondSupportLabel ->
|
||||
Text(
|
||||
text = secondSupportLabel,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
@@ -141,7 +150,7 @@ fun BitwardenListItem(
|
||||
)
|
||||
}
|
||||
|
||||
supportingLabel?.let { supportLabel ->
|
||||
supportingLabel.orNullIfBlank()?.let { supportLabel ->
|
||||
Text(
|
||||
text = supportLabel,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.model
|
||||
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* Defines the possible display options for a card background on a composable component.
|
||||
*/
|
||||
sealed class CardStyle {
|
||||
/**
|
||||
* Indicates if a divider should be displayed in this card.
|
||||
*/
|
||||
abstract val hasDivider: Boolean
|
||||
|
||||
/**
|
||||
* Provides start padding to the divider when present.
|
||||
*/
|
||||
abstract val dividerPadding: Dp
|
||||
|
||||
/**
|
||||
* Defines a top card that has top rounded corners.
|
||||
*/
|
||||
data class Top(
|
||||
override val hasDivider: Boolean = true,
|
||||
override val dividerPadding: Dp = 16.dp,
|
||||
) : CardStyle()
|
||||
|
||||
/**
|
||||
* Defines a middle card that has no rounded corners.
|
||||
*/
|
||||
data class Middle(
|
||||
override val hasDivider: Boolean = true,
|
||||
override val dividerPadding: Dp = 16.dp,
|
||||
) : CardStyle()
|
||||
|
||||
/**
|
||||
* Defines a bottom card that has bottom rounded corners.
|
||||
*/
|
||||
data object Bottom : CardStyle() {
|
||||
override val hasDivider: Boolean = false
|
||||
override val dividerPadding: Dp = 0.dp
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a full card that has rounded corners.
|
||||
*/
|
||||
data object Full : CardStyle() {
|
||||
override val hasDivider: Boolean = false
|
||||
override val dividerPadding: Dp = 0.dp
|
||||
}
|
||||
}
|
||||
+4
@@ -12,6 +12,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.mirrorIfRtl
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -31,6 +32,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* user clicks this item.
|
||||
* @param dialogDismissButtonText The text on the dismiss button of the dialog displayed when the
|
||||
* user clicks this item.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenExternalLinkRow(
|
||||
@@ -43,6 +45,7 @@ fun BitwardenExternalLinkRow(
|
||||
dialogMessage: String,
|
||||
dialogConfirmButtonText: String = stringResource(id = R.string.continue_text),
|
||||
dialogDismissButtonText: String = stringResource(id = R.string.cancel),
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||
BitwardenTextRow(
|
||||
@@ -51,6 +54,7 @@ fun BitwardenExternalLinkRow(
|
||||
onClick = { shouldShowDialog = true },
|
||||
modifier = modifier,
|
||||
withDivider = withDivider,
|
||||
cardStyle = cardStyle,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.mirrorIfRtl(),
|
||||
|
||||
-2
@@ -1,6 +1,5 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.row
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -32,7 +31,6 @@ fun BitwardenRowOfActions(
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.padding(start = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
content = actions,
|
||||
)
|
||||
|
||||
+14
-5
@@ -15,9 +15,13 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -28,10 +32,12 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param onClick The callback when the row is clicked.
|
||||
* @param modifier The modifier to be applied to the layout.
|
||||
* @param description An optional description label to be displayed below the [text].
|
||||
* @param textTestTag The optional test tag for the inner text component.
|
||||
* @param isEnabled Indicates if the row is enabled or not, a disabled row will not be clickable
|
||||
* and it's contents will be dimmed.
|
||||
* @param withDivider Indicates if a divider should be drawn on the bottom of the row, defaults
|
||||
* to `false`.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param content The content of the [BitwardenTextRow].
|
||||
*/
|
||||
@Composable
|
||||
@@ -40,13 +46,17 @@ fun BitwardenTextRow(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
description: String? = null,
|
||||
textTestTag: String? = null,
|
||||
isEnabled: Boolean = true,
|
||||
withDivider: Boolean = false,
|
||||
cardStyle: CardStyle? = null,
|
||||
content: (@Composable () -> Unit)? = null,
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
enabled = isEnabled,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
@@ -55,13 +65,11 @@ fun BitwardenTextRow(
|
||||
),
|
||||
onClick = onClick,
|
||||
)
|
||||
.cardPadding(cardStyle = cardStyle, horizontal = 16.dp)
|
||||
.semantics(mergeDescendants = true) { },
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(start = 16.dp, end = 24.dp, top = 8.dp, bottom = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
@@ -78,6 +86,7 @@ fun BitwardenTextRow(
|
||||
} else {
|
||||
BitwardenTheme.colorScheme.filledButton.foregroundDisabled
|
||||
},
|
||||
modifier = Modifier.run { textTestTag?.let { testTag(it) } ?: this },
|
||||
)
|
||||
description?.let {
|
||||
Text(
|
||||
|
||||
+13
-7
@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.platform.components.slider
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
@@ -22,7 +23,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.focus.focusProperties
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||
@@ -37,8 +37,11 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toDp
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.slider.color.bitwardenSliderColors
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -52,6 +55,7 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @param modifier The [Modifier] to be applied to this radio button.
|
||||
* @param sliderTag The option test tag for the slider component.
|
||||
* @param valueTag The option test tag for the value field component.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("LongMethod")
|
||||
@@ -63,13 +67,18 @@ fun BitwardenSlider(
|
||||
modifier: Modifier = Modifier,
|
||||
sliderTag: String? = null,
|
||||
valueTag: String? = null,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
val sliderValue by rememberUpdatedState(newValue = value.coerceIn(range = range))
|
||||
var labelTextWidth by remember { mutableStateOf(value = Dp.Unspecified) }
|
||||
val density = LocalDensity.current
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier.semantics(mergeDescendants = true) {},
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, end = 16.dp)
|
||||
.semantics(mergeDescendants = true) {},
|
||||
) {
|
||||
TextField(
|
||||
value = sliderValue.toString(),
|
||||
@@ -79,6 +88,7 @@ fun BitwardenSlider(
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.length),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
modifier = Modifier.onGloballyPositioned { layoutCoordinates ->
|
||||
if (labelTextWidth == Dp.Unspecified) {
|
||||
labelTextWidth = layoutCoordinates.size.width.toDp(density = density)
|
||||
@@ -88,11 +98,7 @@ fun BitwardenSlider(
|
||||
},
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
colors = bitwardenTextFieldColors(
|
||||
disabledBorderColor = Color.Transparent,
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
),
|
||||
colors = bitwardenTextFieldColors(),
|
||||
modifier = Modifier
|
||||
.onPreviewKeyEvent { keyEvent ->
|
||||
when (keyEvent.key) {
|
||||
|
||||
+138
-46
@@ -1,14 +1,29 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.stepper
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.ZERO_WIDTH_CHARACTER
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
* Displays a stepper that allows the user to increment and decrement an int value.
|
||||
@@ -19,24 +34,84 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithAc
|
||||
* @param onValueChange callback invoked when the user increments or decrements the count. Note
|
||||
* that this will not be called if the attempts to move value outside of [range].
|
||||
* @param modifier Modifier.
|
||||
* @param supportingText An optional supporting text that will appear below the stepper.
|
||||
* @param range Range of valid values.
|
||||
* @param isIncrementEnabled whether or not the increment button should be enabled.
|
||||
* @param isDecrementEnabled whether or not the decrement button should be enabled.
|
||||
* @param textFieldReadOnly whether or not the text field should be read only. The stepper
|
||||
* increment and decrement buttons function regardless of this value.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun BitwardenStepper(
|
||||
label: String,
|
||||
value: Int?,
|
||||
onValueChange: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingText: String? = null,
|
||||
range: ClosedRange<Int> = 1..Int.MAX_VALUE,
|
||||
isIncrementEnabled: Boolean = true,
|
||||
isDecrementEnabled: Boolean = true,
|
||||
textFieldReadOnly: Boolean = true,
|
||||
stepperActionsTestTag: String? = null,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
BitwardenStepper(
|
||||
modifier = modifier,
|
||||
label = label,
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
supportingTextContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = bitwardenTextFieldColors().focusedSupportingTextColor,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
},
|
||||
range = range,
|
||||
isIncrementEnabled = isIncrementEnabled,
|
||||
isDecrementEnabled = isDecrementEnabled,
|
||||
textFieldReadOnly = textFieldReadOnly,
|
||||
stepperActionsTestTag = stepperActionsTestTag,
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a stepper that allows the user to increment and decrement an int value.
|
||||
*
|
||||
* @param label Label for the stepper.
|
||||
* @param value Value to display. Null will display nothing. Will be clamped to [range] before
|
||||
* display.
|
||||
* @param onValueChange callback invoked when the user increments or decrements the count. Note
|
||||
* that this will not be called if the attempts to move value outside of [range].
|
||||
* @param modifier Modifier.
|
||||
* @param supportingTextContent An optional supporting text composable that will appear below the
|
||||
* stepper.
|
||||
* @param range Range of valid values.
|
||||
* @param isIncrementEnabled whether or not the increment button should be enabled.
|
||||
* @param isDecrementEnabled whether or not the decrement button should be enabled.
|
||||
* @param textFieldReadOnly whether or not the text field should be read only. The stepper
|
||||
* increment and decrement buttons function regardless of this value.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
*/
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun BitwardenStepper(
|
||||
label: String,
|
||||
value: Int?,
|
||||
onValueChange: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
supportingTextContent: @Composable (ColumnScope.() -> Unit)?,
|
||||
range: ClosedRange<Int> = 1..Int.MAX_VALUE,
|
||||
isIncrementEnabled: Boolean = true,
|
||||
isDecrementEnabled: Boolean = true,
|
||||
textFieldReadOnly: Boolean = true,
|
||||
stepperActionsTestTag: String? = null,
|
||||
cardStyle: CardStyle? = null,
|
||||
) {
|
||||
val clampedValue = value?.coerceIn(range)
|
||||
if (clampedValue != value && clampedValue != null) {
|
||||
@@ -44,50 +119,67 @@ fun BitwardenStepper(
|
||||
}
|
||||
val isAtRangeMinimum = clampedValue?.let { (it - 1) !in range } ?: true
|
||||
val isAtRangeMaximum = clampedValue?.let { (it + 1) !in range } ?: false
|
||||
BitwardenTextFieldWithActions(
|
||||
label = label,
|
||||
// We use the zero width character instead of an empty string to make sure label is shown
|
||||
// small and above the input
|
||||
value = clampedValue?.toString() ?: ZERO_WIDTH_CHARACTER,
|
||||
actionsTestTag = stepperActionsTestTag,
|
||||
actions = {
|
||||
BitwardenFilledIconButton(
|
||||
vectorIconRes = R.drawable.ic_minus,
|
||||
contentDescription = "\u2212",
|
||||
onClick = {
|
||||
val decrementedValue = ((clampedValue ?: 0) - 1).coerceIn(range)
|
||||
if (decrementedValue != clampedValue) {
|
||||
onValueChange(decrementedValue)
|
||||
}
|
||||
},
|
||||
isEnabled = isDecrementEnabled && !isAtRangeMinimum,
|
||||
modifier = Modifier.testTag("DecrementValue"),
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, vertical = 8.dp),
|
||||
) {
|
||||
BitwardenTextFieldWithActions(
|
||||
label = label,
|
||||
value = clampedValue?.toString().orEmpty(),
|
||||
actionsTestTag = stepperActionsTestTag,
|
||||
actions = {
|
||||
BitwardenFilledIconButton(
|
||||
vectorIconRes = R.drawable.ic_minus,
|
||||
contentDescription = "\u2212",
|
||||
onClick = {
|
||||
val decrementedValue = ((clampedValue ?: 0) - 1).coerceIn(range)
|
||||
if (decrementedValue != clampedValue) {
|
||||
onValueChange(decrementedValue)
|
||||
}
|
||||
},
|
||||
isEnabled = isDecrementEnabled && !isAtRangeMinimum,
|
||||
modifier = Modifier.testTag("DecrementValue"),
|
||||
)
|
||||
BitwardenFilledIconButton(
|
||||
vectorIconRes = R.drawable.ic_plus,
|
||||
contentDescription = "+",
|
||||
onClick = {
|
||||
val incrementedValue = ((clampedValue ?: 0) + 1).coerceIn(range)
|
||||
if (incrementedValue != clampedValue) {
|
||||
onValueChange(incrementedValue)
|
||||
}
|
||||
},
|
||||
isEnabled = isIncrementEnabled && !isAtRangeMaximum,
|
||||
modifier = Modifier.testTag("IncrementValue"),
|
||||
)
|
||||
},
|
||||
readOnly = textFieldReadOnly,
|
||||
keyboardType = KeyboardType.Number,
|
||||
onValueChange = { newValue ->
|
||||
onValueChange(
|
||||
newValue
|
||||
.orNullIfBlank()
|
||||
?.let { it.toIntOrNull()?.coerceIn(range) ?: clampedValue }
|
||||
?: range.start,
|
||||
)
|
||||
},
|
||||
actionsPadding = PaddingValues(end = 12.dp),
|
||||
)
|
||||
supportingTextContent?.let {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
BitwardenFilledIconButton(
|
||||
vectorIconRes = R.drawable.ic_plus,
|
||||
contentDescription = "+",
|
||||
onClick = {
|
||||
val incrementedValue = ((clampedValue ?: 0) + 1).coerceIn(range)
|
||||
if (incrementedValue != clampedValue) {
|
||||
onValueChange(incrementedValue)
|
||||
}
|
||||
},
|
||||
isEnabled = isIncrementEnabled && !isAtRangeMaximum,
|
||||
modifier = Modifier.testTag("IncrementValue"),
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
content = it,
|
||||
)
|
||||
},
|
||||
readOnly = textFieldReadOnly,
|
||||
keyboardType = KeyboardType.Number,
|
||||
onValueChange = { newValue ->
|
||||
onValueChange(
|
||||
newValue
|
||||
// Make sure the placeholder is gone, since it will mess up the int conversion
|
||||
.replace(ZERO_WIDTH_CHARACTER, "")
|
||||
.orNullIfBlank()
|
||||
?.let { it.toIntOrNull()?.coerceIn(range) ?: clampedValue }
|
||||
?: range.start,
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 4.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -34,6 +34,7 @@ fun BitwardenClickableText(
|
||||
style: TextStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
innerPadding: PaddingValues = PaddingValues(vertical = 4.dp, horizontal = 16.dp),
|
||||
isEnabled: Boolean = true,
|
||||
cornerSize: Dp = 28.dp,
|
||||
color: Color = BitwardenTheme.colorScheme.text.interaction,
|
||||
) {
|
||||
@@ -46,6 +47,7 @@ fun BitwardenClickableText(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
enabled = isEnabled,
|
||||
onClick = onClick,
|
||||
)
|
||||
.padding(innerPadding),
|
||||
|
||||
+86
-50
@@ -2,14 +2,17 @@ package com.x8bit.bitwarden.ui.platform.components.toggle
|
||||
|
||||
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.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
@@ -26,8 +29,12 @@ 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.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
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.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.color.bitwardenSwitchColors
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -38,11 +45,12 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
* @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 supportingText An optional supporting text 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 cardStyle Indicates the type of card style to be applied.
|
||||
* @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.
|
||||
@@ -53,10 +61,11 @@ fun BitwardenSwitch(
|
||||
isChecked: Boolean,
|
||||
onCheckedChange: ((Boolean) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
description: String? = null,
|
||||
supportingText: String? = null,
|
||||
contentDescription: String? = null,
|
||||
readOnly: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
cardStyle: CardStyle? = null,
|
||||
actions: (@Composable RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
BitwardenSwitch(
|
||||
@@ -67,9 +76,10 @@ fun BitwardenSwitch(
|
||||
contentDescription = contentDescription,
|
||||
readOnly = readOnly,
|
||||
enabled = enabled,
|
||||
cardStyle = cardStyle,
|
||||
actions = actions,
|
||||
subContent = {
|
||||
description?.let {
|
||||
supportingTextContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
@@ -91,11 +101,12 @@ fun BitwardenSwitch(
|
||||
* @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 supportingText An optional supporting text 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 cardStyle Indicates the type of card style to be applied.
|
||||
* @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.
|
||||
@@ -106,10 +117,11 @@ fun BitwardenSwitch(
|
||||
isChecked: Boolean,
|
||||
onCheckedChange: ((Boolean) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
description: String? = null,
|
||||
supportingText: String? = null,
|
||||
contentDescription: String? = null,
|
||||
readOnly: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
cardStyle: CardStyle? = null,
|
||||
actions: (@Composable RowScope.() -> Unit)? = null,
|
||||
) {
|
||||
BitwardenSwitch(
|
||||
@@ -120,9 +132,10 @@ fun BitwardenSwitch(
|
||||
contentDescription = contentDescription,
|
||||
readOnly = readOnly,
|
||||
enabled = enabled,
|
||||
cardStyle = cardStyle,
|
||||
actions = actions,
|
||||
subContent = {
|
||||
description?.let {
|
||||
supportingTextContent = supportingText?.let {
|
||||
{
|
||||
Text(
|
||||
text = it,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
@@ -148,10 +161,11 @@ fun BitwardenSwitch(
|
||||
* @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 cardStyle Indicates the type of card style to be applied.
|
||||
* @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.
|
||||
* @param supportingTextContent A lambda containing content directly below the label.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenSwitch(
|
||||
@@ -162,8 +176,9 @@ fun BitwardenSwitch(
|
||||
contentDescription: String? = null,
|
||||
readOnly: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
cardStyle: CardStyle? = null,
|
||||
actions: (@Composable RowScope.() -> Unit)? = null,
|
||||
subContent: @Composable () -> Unit,
|
||||
supportingTextContent: (@Composable ColumnScope.() -> Unit)?,
|
||||
) {
|
||||
BitwardenSwitch(
|
||||
modifier = modifier,
|
||||
@@ -173,8 +188,9 @@ fun BitwardenSwitch(
|
||||
contentDescription = contentDescription,
|
||||
readOnly = readOnly,
|
||||
enabled = enabled,
|
||||
cardStyle = cardStyle,
|
||||
actions = actions,
|
||||
subContent = subContent,
|
||||
supportingTextContent = supportingTextContent,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -189,10 +205,11 @@ fun BitwardenSwitch(
|
||||
* @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 cardStyle Indicates the type of card style to be applied.
|
||||
* @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.
|
||||
* @param supportingTextContent A lambda containing content directly below the label.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
@@ -204,35 +221,44 @@ fun BitwardenSwitch(
|
||||
contentDescription: String? = null,
|
||||
readOnly: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
cardStyle: CardStyle? = null,
|
||||
actions: (@Composable RowScope.() -> Unit)? = null,
|
||||
subContent: @Composable () -> Unit,
|
||||
supportingTextContent: @Composable (ColumnScope.() -> Unit)?,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.wrapContentHeight()
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
),
|
||||
onClick = { onCheckedChange?.invoke(!isChecked) },
|
||||
enabled = !readOnly && enabled,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.run {
|
||||
onCheckedChange
|
||||
?.let {
|
||||
this.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
),
|
||||
onClick = { it(!isChecked) },
|
||||
enabled = !readOnly && enabled,
|
||||
)
|
||||
}
|
||||
?: this
|
||||
}
|
||||
.cardPadding(cardStyle = cardStyle)
|
||||
.semantics(mergeDescendants = true) {
|
||||
toggleableState = ToggleableState(isChecked)
|
||||
contentDescription?.let { this.contentDescription = it }
|
||||
}
|
||||
.then(modifier),
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.weight(weight = 1f),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 36.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(weight = 1f, fill = false)
|
||||
.padding(vertical = 8.dp),
|
||||
Row(
|
||||
modifier = Modifier.weight(weight = 1f),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
@@ -244,23 +270,33 @@ fun BitwardenSwitch(
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "SwitchText"),
|
||||
)
|
||||
subContent()
|
||||
|
||||
actions?.invoke(this)
|
||||
}
|
||||
|
||||
actions
|
||||
?.invoke(this)
|
||||
?: Spacer(modifier = Modifier.width(width = 16.dp))
|
||||
Spacer(modifier = Modifier.width(width = 16.dp))
|
||||
Switch(
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 48.dp)
|
||||
.testTag(tag = "SwitchToggle"),
|
||||
enabled = enabled,
|
||||
checked = isChecked,
|
||||
onCheckedChange = null,
|
||||
colors = bitwardenSwitchColors(),
|
||||
)
|
||||
}
|
||||
supportingTextContent?.let {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
content = it,
|
||||
)
|
||||
}
|
||||
|
||||
Switch(
|
||||
modifier = Modifier
|
||||
.height(height = 56.dp)
|
||||
.testTag(tag = "SwitchToggle"),
|
||||
enabled = enabled,
|
||||
checked = isChecked,
|
||||
onCheckedChange = null,
|
||||
colors = bitwardenSwitchColors(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +306,7 @@ private fun BitwardenSwitch_preview() {
|
||||
Column {
|
||||
BitwardenSwitch(
|
||||
label = "Label",
|
||||
description = "description",
|
||||
supportingText = "description",
|
||||
isChecked = true,
|
||||
onCheckedChange = {},
|
||||
)
|
||||
@@ -281,7 +317,7 @@ private fun BitwardenSwitch_preview() {
|
||||
)
|
||||
BitwardenSwitch(
|
||||
label = "Label",
|
||||
description = "description",
|
||||
supportingText = "description",
|
||||
isChecked = true,
|
||||
onCheckedChange = {},
|
||||
actions = {
|
||||
|
||||
+4
-1
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricSupportStatus
|
||||
|
||||
/**
|
||||
@@ -21,6 +22,7 @@ fun BitwardenUnlockWithBiometricsSwitch(
|
||||
isChecked: Boolean,
|
||||
onDisableBiometrics: () -> Unit,
|
||||
onEnableBiometrics: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val biometricsDescription: String = when (biometricSupportStatus) {
|
||||
@@ -49,6 +51,7 @@ fun BitwardenUnlockWithBiometricsSwitch(
|
||||
}
|
||||
},
|
||||
enabled = biometricSupportStatus == BiometricSupportStatus.CLASS_3_SUPPORTED,
|
||||
description = biometricsDescription,
|
||||
supportingText = biometricsDescription,
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
+4
@@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.PinInputDialog
|
||||
|
||||
/**
|
||||
@@ -19,6 +20,7 @@ import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.PinInput
|
||||
* @param isUnlockWithPinEnabled Indicates whether or not the pin unlocking is enabled.
|
||||
* @param onUnlockWithPinToggleAction Callback that is invoked when the current state of the switch
|
||||
* changes.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param modifier The [Modifier] to be applied to the switch.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@@ -27,6 +29,7 @@ fun BitwardenUnlockWithPinSwitch(
|
||||
isUnlockWithPasswordEnabled: Boolean,
|
||||
isUnlockWithPinEnabled: Boolean,
|
||||
onUnlockWithPinToggleAction: (UnlockWithPinState) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowPinInputDialog by rememberSaveable { mutableStateOf(value = false) }
|
||||
@@ -43,6 +46,7 @@ fun BitwardenUnlockWithPinSwitch(
|
||||
onUnlockWithPinToggleAction(UnlockWithPinState.Disabled)
|
||||
}
|
||||
},
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
|
||||
+20
-12
@@ -6,6 +6,7 @@ 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.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -27,6 +28,7 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
@@ -78,7 +80,7 @@ fun DebugMenuScreen(
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
FeatureFlagContent(
|
||||
featureFlagMap = state.featureFlags,
|
||||
onValueChange = remember(viewModel) {
|
||||
@@ -92,7 +94,9 @@ fun DebugMenuScreen(
|
||||
}
|
||||
},
|
||||
)
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Spacer(Modifier.height(height = 16.dp))
|
||||
BitwardenHorizontalDivider()
|
||||
Spacer(Modifier.height(height = 16.dp))
|
||||
// Pulled these into variable to avoid over-nested formatting in the composable call.
|
||||
val isRestartOnboardingEnabled = state.featureFlags[FlagKey.OnboardingFlow] as? Boolean
|
||||
val isRestartOnboardingCarouselEnabled = state
|
||||
@@ -111,6 +115,7 @@ fun DebugMenuScreen(
|
||||
}
|
||||
},
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -126,22 +131,26 @@ private fun FeatureFlagContent(
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(R.string.feature_flags),
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenHorizontalDivider()
|
||||
featureFlagMap.forEach { featureFlag ->
|
||||
featureFlag.key.ListItemContent(
|
||||
currentValue = featureFlag.value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
cardStyle = featureFlagMap.keys.toListItemCardStyle(
|
||||
index = featureFlagMap.keys.indexOf(element = featureFlag.key),
|
||||
),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
BitwardenHorizontalDivider()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(R.string.reset_values),
|
||||
onClick = onResetValues,
|
||||
@@ -149,7 +158,6 @@ private fun FeatureFlagContent(
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,11 +175,11 @@ private fun OnboardingOverrideContent(
|
||||
Column(modifier) {
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(R.string.onboarding_override),
|
||||
modifier = Modifier.standardHorizontalMargin(),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenHorizontalDivider()
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(R.string.restart_onboarding_cta),
|
||||
onClick = onStartOnboarding,
|
||||
|
||||
+6
-3
@@ -5,6 +5,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
|
||||
/**
|
||||
@@ -15,6 +16,7 @@ import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
currentValue: T,
|
||||
onValueChange: (key: FlagKey<T>, value: T) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) = when (val flagKey = this) {
|
||||
FlagKey.DummyBoolean,
|
||||
@@ -41,6 +43,7 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
key = flagKey as FlagKey<Boolean>,
|
||||
currentValue = currentValue as Boolean,
|
||||
onValueChange = onValueChange as (FlagKey<Boolean>, Boolean) -> Unit,
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -54,14 +57,14 @@ private fun BooleanFlagItem(
|
||||
key: FlagKey<Boolean>,
|
||||
currentValue: Boolean,
|
||||
onValueChange: (key: FlagKey<Boolean>, value: Boolean) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenSwitch(
|
||||
label = label,
|
||||
isChecked = currentValue,
|
||||
onCheckedChange = {
|
||||
onValueChange(key, it)
|
||||
},
|
||||
onCheckedChange = { onValueChange(key, it) },
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package com.x8bit.bitwarden.ui.platform.feature.search
|
||||
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -16,6 +16,8 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenMasterPasswordDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
@@ -107,7 +109,10 @@ fun SearchContent(
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
items(viewState.displayItems) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
itemsIndexed(viewState.displayItems) { index, it ->
|
||||
BitwardenListItem(
|
||||
startIcon = it.iconData,
|
||||
label = it.title,
|
||||
@@ -158,19 +163,19 @@ fun SearchContent(
|
||||
)
|
||||
}
|
||||
.toPersistentList(),
|
||||
cardStyle = viewState.displayItems.toListItemCardStyle(
|
||||
index = index,
|
||||
dividerPadding = 56.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.testTag(searchType.searchItemTestTag)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
// There is some built-in padding to the menu button that makes up
|
||||
// the visual difference here.
|
||||
end = 12.dp,
|
||||
),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
+22
-14
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
@@ -36,10 +37,14 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.mirrorIfRtl
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenMediumTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.badge.NotificationBadge
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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
|
||||
@@ -86,20 +91,22 @@ fun SettingsScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(state = rememberScrollState()),
|
||||
) {
|
||||
Settings.entries.forEach { settingEntry ->
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Settings.entries.forEachIndexed { index, settingEntry ->
|
||||
SettingsRow(
|
||||
text = settingEntry.text,
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(SettingsAction.SettingsClick(settingEntry)) }
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag(settingEntry.testTag)
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
notificationCount = state.notificationBadgeCountMap.getOrDefault(
|
||||
key = settingEntry,
|
||||
defaultValue = 0,
|
||||
),
|
||||
cardStyle = Settings.entries.toListItemCardStyle(index = index),
|
||||
modifier = Modifier
|
||||
.testTag(tag = settingEntry.testTag)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -110,11 +117,14 @@ fun SettingsScreen(
|
||||
private fun SettingsRow(
|
||||
text: Text,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
notificationCount: Int,
|
||||
cardStyle: CardStyle?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
@@ -122,17 +132,14 @@ private fun SettingsRow(
|
||||
),
|
||||
onClick = onClick,
|
||||
)
|
||||
.bottomDivider(paddingStart = 16.dp)
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(end = 8.dp, top = 8.dp, bottom = 8.dp)
|
||||
.then(modifier),
|
||||
.cardPadding(cardStyle = cardStyle, start = 16.dp, end = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.weight(1f),
|
||||
.weight(weight = 1f),
|
||||
text = text(),
|
||||
style = BitwardenTheme.typography.bodyLarge,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
@@ -164,7 +171,7 @@ private fun TrailingContent(
|
||||
tint = BitwardenTheme.colorScheme.icon.primary,
|
||||
modifier = Modifier
|
||||
.mirrorIfRtl()
|
||||
.size(24.dp),
|
||||
.size(size = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -185,6 +192,7 @@ private fun SettingsRows_preview() {
|
||||
text = it.text,
|
||||
onClick = { },
|
||||
notificationCount = index % 3,
|
||||
cardStyle = Settings.entries.toListItemCardStyle(index = index),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+60
-58
@@ -1,11 +1,7 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -19,18 +15,14 @@ import androidx.compose.material3.Icon
|
||||
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
|
||||
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.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
@@ -40,9 +32,12 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.mirrorIfRtl
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenExternalLinkRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -161,72 +156,104 @@ private fun ContentColumn(
|
||||
modifier = modifier
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
if (state.shouldShowCrashLogsButton) {
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.submit_crash_logs),
|
||||
contentDescription = stringResource(id = R.string.submit_crash_logs),
|
||||
isChecked = state.isSubmitCrashLogsEnabled,
|
||||
onCheckedChange = onSubmitCrashLogsCheckedChange,
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.testTag("SubmitCrashLogsSwitch")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
contentDescription = stringResource(id = R.string.submit_crash_logs),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
BitwardenExternalLinkRow(
|
||||
text = stringResource(id = R.string.bitwarden_help_center),
|
||||
onConfirmClick = onHelpCenterClick,
|
||||
dialogTitle = stringResource(id = R.string.continue_to_help_center),
|
||||
modifier = Modifier.testTag("BitwardenHelpCenterRow"),
|
||||
dialogMessage = stringResource(
|
||||
id = R.string.learn_more_about_how_to_use_bitwarden_on_the_help_center,
|
||||
),
|
||||
withDivider = false,
|
||||
cardStyle = if (state.shouldShowCrashLogsButton) {
|
||||
CardStyle.Middle()
|
||||
} else {
|
||||
CardStyle.Top()
|
||||
},
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag(tag = "BitwardenHelpCenterRow"),
|
||||
)
|
||||
BitwardenExternalLinkRow(
|
||||
text = stringResource(id = R.string.privacy_policy),
|
||||
onConfirmClick = onPrivacyPolicyClick,
|
||||
dialogTitle = stringResource(id = R.string.continue_to_privacy_policy),
|
||||
modifier = Modifier.testTag("PrivacyPolicyRow"),
|
||||
dialogMessage = stringResource(
|
||||
id = R.string.privacy_policy_description_long,
|
||||
),
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag(tag = "PrivacyPolicyRow"),
|
||||
)
|
||||
BitwardenExternalLinkRow(
|
||||
text = stringResource(id = R.string.web_vault),
|
||||
onConfirmClick = onWebVaultClick,
|
||||
modifier = Modifier.testTag("BitwardenWebVaultRow"),
|
||||
dialogTitle = stringResource(id = R.string.continue_to_web_app),
|
||||
dialogMessage = stringResource(
|
||||
id = R.string.explore_more_features_of_your_bitwarden_account_on_the_web_app,
|
||||
),
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag(tag = "BitwardenWebVaultRow"),
|
||||
)
|
||||
BitwardenExternalLinkRow(
|
||||
text = stringResource(id = R.string.learn_org),
|
||||
onConfirmClick = onLearnAboutOrgsClick,
|
||||
dialogTitle = stringResource(id = R.string.continue_to_x, "bitwarden.com"),
|
||||
modifier = Modifier.testTag("LearnAboutOrganizationsRow"),
|
||||
dialogMessage = stringResource(
|
||||
id = R.string.learn_about_organizations_description_long,
|
||||
),
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag(tag = "LearnAboutOrganizationsRow"),
|
||||
)
|
||||
BitwardenExternalLinkRow(
|
||||
text = stringResource(R.string.give_feedback),
|
||||
onConfirmClick = onGiveFeedbackClick,
|
||||
modifier = Modifier.testTag("GiveFeedbackRow"),
|
||||
dialogTitle = stringResource(R.string.continue_to_give_feedback),
|
||||
dialogMessage = stringResource(R.string.continue_to_provide_feedback),
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag(tag = "GiveFeedbackRow"),
|
||||
)
|
||||
CopyRow(
|
||||
text = state.version,
|
||||
onClick = onVersionClick,
|
||||
modifier = Modifier.testTag("CopyAboutInfoRow"),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag("CopyAboutInfoRow"),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
@@ -246,44 +273,19 @@ private fun CopyRow(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
modifier = modifier
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
),
|
||||
onClick = onClick,
|
||||
)
|
||||
.semantics(mergeDescendants = true) {
|
||||
contentDescription = text.toString(resources)
|
||||
},
|
||||
BitwardenTextRow(
|
||||
text = text(),
|
||||
onClick = onClick,
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(start = 16.dp, end = 24.dp, top = 8.dp, bottom = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.weight(1f),
|
||||
text = text(),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
)
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = R.drawable.ic_copy),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.primary,
|
||||
)
|
||||
}
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = R.drawable.ic_copy),
|
||||
contentDescription = null,
|
||||
tint = BitwardenTheme.colorScheme.icon.primary,
|
||||
modifier = Modifier.mirrorIfRtl(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+52
-10
@@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -51,6 +52,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTimePickerDial
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenExternalLinkRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
@@ -177,6 +179,7 @@ fun AccountSecurityScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
AnimatedVisibility(
|
||||
visible = state.shouldShowUnlockActionCard,
|
||||
label = "UnlockActionCard",
|
||||
@@ -200,7 +203,7 @@ fun AccountSecurityScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.padding(top = 12.dp, bottom = 16.dp),
|
||||
.padding(bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -208,15 +211,19 @@ fun AccountSecurityScreen(
|
||||
label = stringResource(id = R.string.approve_login_requests),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.pending_log_in_requests),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.PendingLoginRequestsClick) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("PendingLogInRequestsLabel")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -225,10 +232,14 @@ fun AccountSecurityScreen(
|
||||
label = stringResource(id = R.string.unlock_options),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
val biometricSupportStatus = biometricsManager.biometricSupportStatus
|
||||
BitwardenUnlockWithBiometricsSwitch(
|
||||
biometricSupportStatus = biometricsManager.biometricSupportStatus,
|
||||
biometricSupportStatus = biometricSupportStatus,
|
||||
isChecked = state.isUnlockWithBiometricsEnabled || showBiometricsPrompt,
|
||||
onDisableBiometrics = remember(viewModel) {
|
||||
{
|
||||
@@ -240,21 +251,24 @@ fun AccountSecurityScreen(
|
||||
onEnableBiometrics = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.EnableBiometricsClick) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("UnlockWithBiometricsSwitch")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenUnlockWithPinSwitch(
|
||||
isUnlockWithPasswordEnabled = state.isUnlockWithPasswordEnabled,
|
||||
isUnlockWithPinEnabled = state.isUnlockWithPinEnabled,
|
||||
onUnlockWithPinToggleAction = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.UnlockWithPinToggle(it)) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("UnlockWithPinSwitch")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
if (state.shouldShowEnableAuthenticatorSync) {
|
||||
@@ -274,14 +288,16 @@ fun AccountSecurityScreen(
|
||||
label = stringResource(id = R.string.session_timeout),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
SessionTimeoutPolicyRow(
|
||||
vaultTimeoutPolicyMinutes = state.vaultTimeoutPolicyMinutes,
|
||||
vaultTimeoutPolicyAction = state.vaultTimeoutPolicyAction,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
SessionTimeoutRow(
|
||||
vaultTimeoutPolicyMinutes = state.vaultTimeoutPolicyMinutes,
|
||||
@@ -291,7 +307,8 @@ fun AccountSecurityScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("VaultTimeoutChooser")
|
||||
.fillMaxWidth(),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
(state.vaultTimeout as? VaultTimeout.Custom)?.let { customTimeout ->
|
||||
SessionCustomTimeoutRow(
|
||||
@@ -304,7 +321,9 @@ fun AccountSecurityScreen(
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
SessionTimeoutActionRow(
|
||||
@@ -316,7 +335,8 @@ fun AccountSecurityScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("VaultTimeoutActionChooser")
|
||||
.fillMaxWidth(),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
@@ -324,15 +344,19 @@ fun AccountSecurityScreen(
|
||||
label = stringResource(id = R.string.other),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.account_fingerprint_phrase),
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.AccountFingerprintPhraseClick) }
|
||||
},
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.testTag("AccountFingerprintPhraseLabel")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
BitwardenExternalLinkRow(
|
||||
@@ -343,8 +367,10 @@ fun AccountSecurityScreen(
|
||||
withDivider = false,
|
||||
dialogTitle = stringResource(id = R.string.continue_to_web_app),
|
||||
dialogMessage = stringResource(id = R.string.two_step_login_description_long),
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("TwoStepLoginLinkItemView")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
if (state.isUnlockWithPasswordEnabled) {
|
||||
@@ -358,7 +384,10 @@ fun AccountSecurityScreen(
|
||||
dialogMessage = stringResource(
|
||||
id = R.string.change_master_password_description_long,
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
if (state.hasUnlockMechanism) {
|
||||
@@ -367,8 +396,10 @@ fun AccountSecurityScreen(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.LockNowClick) }
|
||||
},
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("LockNowLabel")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
@@ -377,8 +408,10 @@ fun AccountSecurityScreen(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.LogoutClick) }
|
||||
},
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("LogOutLabel")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
BitwardenTextRow(
|
||||
@@ -386,10 +419,13 @@ fun AccountSecurityScreen(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.DeleteAccountClick) }
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("DeleteAccountLabel")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,6 +510,7 @@ private fun SessionTimeoutRow(
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.session_timeout),
|
||||
onClick = { shouldShowSelectionDialog = true },
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
@@ -545,6 +582,7 @@ private fun SessionCustomTimeoutRow(
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.custom),
|
||||
onClick = { shouldShowTimePickerDialog = true },
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = modifier,
|
||||
) {
|
||||
val formattedTime = LocalTime
|
||||
@@ -625,6 +663,7 @@ private fun SessionTimeoutActionRow(
|
||||
if (vaultTimeoutPolicyAction != null) return@BitwardenTextRow
|
||||
shouldShowSelectionDialog = true
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
@@ -685,7 +724,7 @@ private fun SessionTimeoutActionRow(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SyncWithAuthenticatorRow(
|
||||
private fun ColumnScope.SyncWithAuthenticatorRow(
|
||||
isChecked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
) {
|
||||
@@ -693,12 +732,15 @@ private fun SyncWithAuthenticatorRow(
|
||||
label = stringResource(R.string.authenticator_sync),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(R.string.allow_bitwarden_authenticator_syncing),
|
||||
onCheckedChange = onCheckedChange,
|
||||
isChecked = isChecked,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
||||
+3
-2
@@ -34,6 +34,7 @@ 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.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.maxDialogHeight
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -115,9 +116,9 @@ fun PinInputDialog(
|
||||
pin = newValue.filter { it.isDigit() || !isPinCreation }
|
||||
},
|
||||
keyboardType = KeyboardType.Number,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
textFieldTestTag = "AlertInputField",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
}
|
||||
|
||||
+28
-19
@@ -11,15 +11,16 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
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.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -49,8 +50,10 @@ import com.x8bit.bitwarden.data.platform.util.isBuildVersionBelow
|
||||
import com.x8bit.bitwarden.data.platform.util.isFdroid
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.LivecycleEventEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
@@ -58,6 +61,7 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.rememberBitwardenPullToRefreshState
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -223,20 +227,27 @@ private fun PendingRequestsContent(
|
||||
LazyColumn(
|
||||
modifier = Modifier.weight(weight = 1f, fill = false),
|
||||
) {
|
||||
items(state.requests) { request ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
itemsIndexed(state.requests) { index, request ->
|
||||
PendingRequestItem(
|
||||
fingerprintPhrase = request.fingerprintPhrase,
|
||||
platform = request.platform,
|
||||
timestamp = request.timestamp,
|
||||
onNavigateToLoginApproval = onNavigateToLoginApproval,
|
||||
cardStyle = state.requests.toListItemCardStyle(
|
||||
index = index,
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.testTag("LoginRequestCell")
|
||||
.fillMaxWidth()
|
||||
.bottomDivider(),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
|
||||
BitwardenOutlinedButton(
|
||||
label = stringResource(id = R.string.decline_all_requests),
|
||||
@@ -244,9 +255,11 @@ private fun PendingRequestsContent(
|
||||
onClick = { shouldShowDeclineAllRequestsConfirm = true },
|
||||
modifier = Modifier
|
||||
.testTag("DeclineAllRequestsButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -260,29 +273,29 @@ private fun PendingRequestItem(
|
||||
platform: String,
|
||||
timestamp: String,
|
||||
onNavigateToLoginApproval: (fingerprintPhrase: String) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
),
|
||||
onClick = { onNavigateToLoginApproval(fingerprintPhrase) },
|
||||
),
|
||||
)
|
||||
.cardPadding(cardStyle = cardStyle, horizontal = 16.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.fingerprint_phrase),
|
||||
style = BitwardenTheme.typography.labelMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
textAlign = TextAlign.Start,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -294,8 +307,7 @@ private fun PendingRequestItem(
|
||||
textAlign = TextAlign.Start,
|
||||
modifier = Modifier
|
||||
.testTag("FingerprintValueLabel")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@@ -310,18 +322,15 @@ private fun PendingRequestItem(
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
textAlign = TextAlign.Start,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(width = 16.dp))
|
||||
Text(
|
||||
text = timestamp,
|
||||
style = BitwardenTheme.typography.labelSmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
textAlign = TextAlign.End,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-5
@@ -1,9 +1,11 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.appearance
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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
|
||||
@@ -26,10 +28,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
@@ -78,6 +82,7 @@ fun AppearanceScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
LanguageSelectionRow(
|
||||
currentSelection = state.language,
|
||||
onLanguageSelection = remember(viewModel) {
|
||||
@@ -85,9 +90,10 @@ fun AppearanceScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("LanguageChooser")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
ThemeSelectionRow(
|
||||
currentSelection = state.theme,
|
||||
onThemeSelection = remember(viewModel) {
|
||||
@@ -95,21 +101,24 @@ fun AppearanceScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ThemeChooser")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.show_website_icons),
|
||||
description = stringResource(id = R.string.show_website_icons_description),
|
||||
supportingText = stringResource(id = R.string.show_website_icons_description),
|
||||
isChecked = state.showWebsiteIcons,
|
||||
onCheckedChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AppearanceAction.ShowWebsiteIconsToggle(it)) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("ShowWebsiteIconsSwitch")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,6 +142,7 @@ private fun LanguageSelectionRow(
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.language),
|
||||
onClick = { shouldShowLanguageSelectionDialog = true },
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
@@ -174,6 +184,7 @@ private fun ThemeSelectionRow(
|
||||
text = stringResource(id = R.string.theme),
|
||||
description = stringResource(id = R.string.theme_description),
|
||||
onClick = { shouldShowThemeSelectionDialog = true },
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
|
||||
+38
-12
@@ -41,6 +41,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialo
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenExternalLinkRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
@@ -128,6 +129,7 @@ fun AutoFillScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
AnimatedVisibility(
|
||||
visible = state.showAutofillActionCard,
|
||||
label = "AutofillActionCard",
|
||||
@@ -151,31 +153,35 @@ fun AutoFillScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.padding(top = 12.dp, bottom = 16.dp),
|
||||
.padding(bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.autofill),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.autofill_services),
|
||||
description = stringResource(id = R.string.autofill_services_explanation_long),
|
||||
supportingText = stringResource(id = R.string.autofill_services_explanation_long),
|
||||
isChecked = state.isAutoFillServicesEnabled,
|
||||
onCheckedChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AutoFillAction.AutoFillServicesClick(it)) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("AutofillServicesSwitch")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
if (state.showInlineAutofillOption) {
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.inline_autofill),
|
||||
description = stringResource(
|
||||
supportingText = stringResource(
|
||||
id = R.string.use_inline_autofill_explanation_long,
|
||||
),
|
||||
isChecked = state.isUseInlineAutoFillEnabled,
|
||||
@@ -183,11 +189,13 @@ fun AutoFillScreen(
|
||||
{ viewModel.trySendAction(AutoFillAction.UseInlineAutofillClick(it)) }
|
||||
},
|
||||
enabled = state.canInteractWithInlineAutofillToggle,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("InlineAutofillSwitch")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
if (state.showPasskeyManagementRow) {
|
||||
BitwardenExternalLinkRow(
|
||||
@@ -203,7 +211,12 @@ fun AutoFillScreen(
|
||||
id = R.string.set_bitwarden_as_passkey_manager_description,
|
||||
),
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
AccessibilityAutofillSwitch(
|
||||
isAccessibilityAutoFillEnabled = state.isAccessibilityAutofillEnabled,
|
||||
@@ -212,39 +225,45 @@ fun AutoFillScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.additional_options),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.copy_totp_automatically),
|
||||
description = stringResource(id = R.string.copy_totp_automatically_description),
|
||||
supportingText = stringResource(id = R.string.copy_totp_automatically_description),
|
||||
isChecked = state.isCopyTotpAutomaticallyEnabled,
|
||||
onCheckedChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AutoFillAction.CopyTotpAutomaticallyClick(it)) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("CopyTotpAutomaticallySwitch")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.ask_to_add_login),
|
||||
description = stringResource(id = R.string.ask_to_add_login_description),
|
||||
supportingText = stringResource(id = R.string.ask_to_add_login_description),
|
||||
isChecked = state.isAskToAddLoginEnabled,
|
||||
onCheckedChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AutoFillAction.AskToAddLoginClick(it)) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("AskToAddLoginSwitch")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
DefaultUriMatchTypeRow(
|
||||
selectedUriMatchType = state.defaultUriMatchType,
|
||||
onUriMatchTypeSelect = remember(viewModel) {
|
||||
@@ -252,8 +271,10 @@ fun AutoFillScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("DefaultUriMatchDetectionChooser")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextRow(
|
||||
text = stringResource(id = R.string.block_auto_fill),
|
||||
description = stringResource(
|
||||
@@ -262,7 +283,10 @@ fun AutoFillScreen(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AutoFillAction.BlockAutoFillClick) }
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
@@ -278,7 +302,7 @@ private fun AccessibilityAutofillSwitch(
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(value = false) }
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.accessibility),
|
||||
description = stringResource(id = R.string.accessibility_description5),
|
||||
supportingText = stringResource(id = R.string.accessibility_description5),
|
||||
isChecked = isAccessibilityAutoFillEnabled,
|
||||
onCheckedChange = {
|
||||
if (isAccessibilityAutoFillEnabled) {
|
||||
@@ -287,6 +311,7 @@ private fun AccessibilityAutofillSwitch(
|
||||
shouldShowDialog = true
|
||||
}
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier.testTag(tag = "AccessibilityAutofillSwitch"),
|
||||
)
|
||||
|
||||
@@ -318,6 +343,7 @@ private fun DefaultUriMatchTypeRow(
|
||||
text = stringResource(id = R.string.default_uri_match_detection),
|
||||
description = stringResource(id = R.string.default_uri_match_detection_description),
|
||||
onClick = { shouldShowDialog = true },
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
|
||||
+5
-2
@@ -21,10 +21,12 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.x8bit.bitwarden.R
|
||||
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.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.maxDialogHeight
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -82,15 +84,16 @@ fun AddEditBlockedUriDialog(
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.enter_uri),
|
||||
isError = errorMessage != null,
|
||||
hint = errorMessage ?: stringResource(
|
||||
supportingText = errorMessage ?: stringResource(
|
||||
id = R.string.format_x_separate_multiple_ur_is_with_a_comma,
|
||||
"http://domain.com",
|
||||
),
|
||||
value = uri,
|
||||
onValueChange = onUriChange,
|
||||
keyboardType = KeyboardType.Uri,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -249,6 +249,7 @@ private fun BlockAutoFillListItem(
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
@@ -257,7 +258,6 @@ private fun BlockAutoFillListItem(
|
||||
onClick = onClick,
|
||||
)
|
||||
.bottomDivider(paddingStart = 16.dp)
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(end = 8.dp, top = 16.dp, bottom = 16.dp)
|
||||
.then(modifier),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
|
||||
+38
-40
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -35,6 +34,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.auth.feature.completeregistration.PasswordStrengthIndicator
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
@@ -43,6 +43,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
@@ -194,13 +195,13 @@ private fun ExportVaultScreenContent(
|
||||
.imePadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
if (state.policyPreventsExport) {
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = R.string.disable_personal_vault_export_policy_in_effect),
|
||||
modifier = Modifier
|
||||
.testTag("DisablePrivateVaultPolicyLabel")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -219,15 +220,15 @@ private fun ExportVaultScreenContent(
|
||||
onExportFormatOptionSelected(selectedOption)
|
||||
},
|
||||
isEnabled = !state.policyPreventsExport,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("FileFormatPicker")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (state.exportFormat == ExportVaultFormat.JSON_ENCRYPTED) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
var showPassword by rememberSaveable { mutableStateOf(false) }
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.file_password),
|
||||
@@ -235,20 +236,27 @@ private fun ExportVaultScreenContent(
|
||||
onValueChange = onFilePasswordInputChanged,
|
||||
showPassword = showPassword,
|
||||
showPasswordChange = { showPassword = it },
|
||||
hint = stringResource(id = R.string.password_used_to_export),
|
||||
supportingTextContent = {
|
||||
PasswordStrengthIndicator(
|
||||
state = state.passwordStrengthState,
|
||||
currentCharacterCount = state.passwordInput.length,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.password_used_to_export),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("FilePasswordEntry")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
PasswordStrengthIndicator(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
state = state.passwordStrengthState,
|
||||
currentCharacterCount = state.passwordInput.length,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.confirm_file_password),
|
||||
@@ -256,16 +264,15 @@ private fun ExportVaultScreenContent(
|
||||
onValueChange = onConfirmFilePasswordInputChanged,
|
||||
showPassword = showPassword,
|
||||
showPasswordChange = { showPassword = it },
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("ConfirmFilePasswordEntry")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
if (state.showSendCodeButton) {
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
@@ -274,7 +281,7 @@ private fun ExportVaultScreenContent(
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -286,7 +293,7 @@ private fun ExportVaultScreenContent(
|
||||
isEnabled = !state.policyPreventsExport,
|
||||
modifier = Modifier
|
||||
.testTag("SendTOTPCodeButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -296,49 +303,40 @@ private fun ExportVaultScreenContent(
|
||||
label = stringResource(id = R.string.verification_code),
|
||||
value = state.passwordInput,
|
||||
readOnly = state.policyPreventsExport,
|
||||
hint = stringResource(id = R.string.confirm_your_identity),
|
||||
supportingText = stringResource(id = R.string.confirm_your_identity),
|
||||
onValueChange = onPasswordInputChanged,
|
||||
keyboardType = KeyboardType.Number,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
} else {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.master_password),
|
||||
supportingText = stringResource(
|
||||
id = R.string.export_vault_master_password_description,
|
||||
),
|
||||
value = state.passwordInput,
|
||||
readOnly = state.policyPreventsExport,
|
||||
onValueChange = onPasswordInputChanged,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordEntry")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.export_vault_master_password_description),
|
||||
textAlign = TextAlign.Start,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenOutlinedButton(
|
||||
label = stringResource(id = R.string.export_vault),
|
||||
onClick = onExportVaultClick,
|
||||
isEnabled = !state.policyPreventsExport,
|
||||
modifier = Modifier
|
||||
.testTag("ExportVaultButton")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
+24
-34
@@ -1,22 +1,19 @@
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.folders
|
||||
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
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.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
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.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -31,11 +28,13 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.fab.BitwardenFloatingActionButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.folders.model.FolderDisplayItem
|
||||
@@ -150,33 +149,24 @@ private fun FoldersContent(
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
items(foldersList) {
|
||||
Row(
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
itemsIndexed(foldersList) { index, it ->
|
||||
BitwardenTextRow(
|
||||
text = it.name,
|
||||
onClick = { onItemClick(it.id) },
|
||||
textTestTag = "FolderName",
|
||||
cardStyle = foldersList.toListItemCardStyle(index = index),
|
||||
modifier = Modifier
|
||||
.testTag("FolderCell")
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(
|
||||
color = BitwardenTheme.colorScheme.background.pressed,
|
||||
),
|
||||
onClick = { onItemClick(it.id) },
|
||||
)
|
||||
.bottomDivider(paddingStart = 16.dp)
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.testTag("FolderName")
|
||||
.padding(start = 16.dp)
|
||||
.weight(1f),
|
||||
text = it.name,
|
||||
style = BitwardenTheme.typography.bodyLarge,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
)
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.testTag(tag = "FolderCell"),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 88.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-2
@@ -2,9 +2,11 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.folders.addedit
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
@@ -24,6 +26,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.OverflowMenuItemData
|
||||
@@ -34,6 +37,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
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
|
||||
@@ -127,6 +131,7 @@ fun FolderAddEditScreen(
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = viewState.folderName,
|
||||
@@ -134,10 +139,12 @@ fun FolderAddEditScreen(
|
||||
{ viewModel.trySendAction(FolderAddEditAction.NameTextChange(it)) }
|
||||
},
|
||||
textFieldTestTag = "FolderNameField",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-8
@@ -8,6 +8,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.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -33,6 +34,7 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.ClearClipboardFrequency
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.displayLabel
|
||||
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.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog
|
||||
@@ -40,6 +42,7 @@ import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
@@ -102,20 +105,22 @@ fun OtherScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.enable_sync_on_refresh),
|
||||
description = stringResource(id = R.string.enable_sync_on_refresh_description),
|
||||
supportingText = stringResource(id = R.string.enable_sync_on_refresh_description),
|
||||
isChecked = state.allowSyncOnRefresh,
|
||||
onCheckedChange = remember(viewModel) {
|
||||
{ viewModel.trySendAction(OtherAction.AllowSyncToggle(it)) }
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("SyncOnRefreshSwitch")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
|
||||
BitwardenOutlinedButton(
|
||||
onClick = remember(viewModel) {
|
||||
@@ -125,10 +130,10 @@ fun OtherScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("SyncNowButton")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -151,7 +156,7 @@ fun OtherScreen(
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
|
||||
ClearClipboardFrequencyRow(
|
||||
currentSelection = state.clearClipboardFrequency,
|
||||
@@ -160,9 +165,12 @@ fun OtherScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ClearClipboardChooser")
|
||||
.fillMaxWidth(),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
|
||||
ScreenCaptureRow(
|
||||
currentValue = state.allowScreenCapture,
|
||||
onValueChange = remember(viewModel) {
|
||||
@@ -171,8 +179,10 @@ fun OtherScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("AllowScreenCaptureSwitch")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,6 +205,7 @@ private fun ScreenCaptureRow(
|
||||
shouldShowScreenCaptureConfirmDialog = true
|
||||
}
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
@@ -226,6 +237,7 @@ private fun ClearClipboardFrequencyRow(
|
||||
text = stringResource(id = R.string.clear_clipboard),
|
||||
description = stringResource(id = R.string.clear_clipboard_description),
|
||||
onClick = { shouldShowClearClipboardDialog = true },
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Text(
|
||||
|
||||
+18
-7
@@ -3,8 +3,10 @@ package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
import android.widget.Toast
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -30,6 +32,7 @@ import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.badge.NotificationBadge
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenActionCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.actionCardExitAnimation
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenExternalLinkRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenTextRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
@@ -106,6 +109,7 @@ fun VaultSettingsScreen(
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
AnimatedVisibility(
|
||||
visible = state.shouldShowImportCard,
|
||||
label = "ImportLoginsActionCard",
|
||||
@@ -132,7 +136,7 @@ fun VaultSettingsScreen(
|
||||
},
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.padding(top = 12.dp, bottom = 16.dp),
|
||||
.padding(bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
BitwardenTextRow(
|
||||
@@ -140,9 +144,11 @@ fun VaultSettingsScreen(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.FoldersButtonClick) }
|
||||
},
|
||||
withDivider = true,
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.testTag("FoldersLabel")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -151,9 +157,11 @@ fun VaultSettingsScreen(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ExportVaultClick) }
|
||||
},
|
||||
withDivider = true,
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("ExportVaultLabel")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -163,9 +171,11 @@ fun VaultSettingsScreen(
|
||||
onClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||
},
|
||||
withDivider = true,
|
||||
withDivider = false,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("ImportItemsLinkItemView")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
} else {
|
||||
@@ -174,15 +184,16 @@ fun VaultSettingsScreen(
|
||||
onConfirmClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultSettingsAction.ImportItemsClick) }
|
||||
},
|
||||
withDivider = true,
|
||||
withDivider = false,
|
||||
dialogTitle = stringResource(id = R.string.continue_to_web_app),
|
||||
dialogMessage =
|
||||
stringResource(
|
||||
dialogMessage = stringResource(
|
||||
id = R.string.you_can_import_data_to_your_vault_on_x,
|
||||
state.importUrl,
|
||||
),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("ImportItemsLinkItemView")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@ data class BitwardenShapes(
|
||||
val coachmark: CornerBasedShape,
|
||||
val content: CornerBasedShape,
|
||||
val contentBottom: CornerBasedShape,
|
||||
val contentMiddle: CornerBasedShape,
|
||||
val contentTop: CornerBasedShape,
|
||||
val dialog: CornerBasedShape,
|
||||
val fab: CornerBasedShape,
|
||||
val infoCallout: CornerBasedShape,
|
||||
val menu: CornerBasedShape,
|
||||
val progressIndicator: CornerBasedShape,
|
||||
val segmentedControl: CornerBasedShape,
|
||||
val snackbar: CornerBasedShape,
|
||||
)
|
||||
@@ -13,11 +13,13 @@ val bitwardenShapes: BitwardenShapes = BitwardenShapes(
|
||||
coachmark = RoundedCornerShape(size = 8.dp),
|
||||
content = RoundedCornerShape(size = 8.dp),
|
||||
contentBottom = RoundedCornerShape(bottomStart = 8.dp, bottomEnd = 8.dp),
|
||||
contentMiddle = RoundedCornerShape(0.dp),
|
||||
contentTop = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp),
|
||||
dialog = RoundedCornerShape(size = 28.dp),
|
||||
fab = CircleShape,
|
||||
infoCallout = RoundedCornerShape(size = 8.dp),
|
||||
menu = RoundedCornerShape(size = 4.dp),
|
||||
progressIndicator = CircleShape,
|
||||
segmentedControl = CircleShape,
|
||||
snackbar = RoundedCornerShape(size = 8.dp),
|
||||
)
|
||||
+171
-158
@@ -8,7 +8,8 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -35,19 +36,20 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.LivecycleEventEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.scrolledContainerBottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenMediumTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.OverflowMenuItemData
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
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.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TextToolbarType
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TooltipData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.TopAppBarDividerStyle
|
||||
@@ -301,6 +303,7 @@ private fun ScrollContent(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxHeight()
|
||||
.imePadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@@ -309,7 +312,7 @@ private fun ScrollContent(
|
||||
text = stringResource(id = R.string.password_generator_policy_in_effect),
|
||||
modifier = Modifier
|
||||
.testTag("PasswordGeneratorPolicyInEffectLabel")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -320,7 +323,7 @@ private fun ScrollContent(
|
||||
generatedText = state.generatedText,
|
||||
onRegenerateClick = onRegenerateClick,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
@@ -332,20 +335,11 @@ private fun ScrollContent(
|
||||
modifier = Modifier
|
||||
.testTag(tag = "CopyValueButton")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.options),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
when (val selectedType = state.selectedType) {
|
||||
is GeneratorState.MainType.Passphrase -> {
|
||||
PassphraseTypeContent(
|
||||
@@ -373,6 +367,8 @@ private fun ScrollContent(
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +384,7 @@ private fun GeneratedStringItem(
|
||||
value = generatedText,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_generate,
|
||||
contentDescription = stringResource(id = R.string.generate_password),
|
||||
onClick = onRegenerateClick,
|
||||
@@ -402,6 +398,7 @@ private fun GeneratedStringItem(
|
||||
visualTransformation = nonLetterColorVisualTransformation(),
|
||||
modifier = modifier,
|
||||
textToolbarType = TextToolbarType.NONE,
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -459,8 +456,6 @@ private fun ColumnScope.PasswordTypeContent(
|
||||
passwordTypeState: GeneratorState.MainType.Password,
|
||||
passwordHandlers: PasswordHandlers,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
BitwardenSlider(
|
||||
value = passwordTypeState.length,
|
||||
onValueChange = { newValue, isUserInteracting ->
|
||||
@@ -471,41 +466,58 @@ private fun ColumnScope.PasswordTypeContent(
|
||||
range = passwordTypeState.minLength..passwordTypeState.maxLength,
|
||||
sliderTag = "PasswordLengthSlider",
|
||||
valueTag = "PasswordLengthLabel",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 28.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
|
||||
PasswordCapitalLettersToggleItem(
|
||||
useCapitals = passwordTypeState.useCapitals,
|
||||
onPasswordToggleCapitalLettersChange = passwordHandlers
|
||||
.onPasswordToggleCapitalLettersChange,
|
||||
enabled = passwordTypeState.capitalsEnabled,
|
||||
)
|
||||
PasswordLowercaseLettersToggleItem(
|
||||
useLowercase = passwordTypeState.useLowercase,
|
||||
onPasswordToggleLowercaseLettersChange = passwordHandlers
|
||||
.onPasswordToggleLowercaseLettersChange,
|
||||
enabled = passwordTypeState.lowercaseEnabled,
|
||||
)
|
||||
PasswordNumbersToggleItem(
|
||||
useNumbers = passwordTypeState.useNumbers,
|
||||
onPasswordToggleNumbersChange = passwordHandlers.onPasswordToggleNumbersChange,
|
||||
enabled = passwordTypeState.numbersEnabled,
|
||||
)
|
||||
PasswordSpecialCharactersToggleItem(
|
||||
useSpecialChars = passwordTypeState.useSpecialChars,
|
||||
onPasswordToggleSpecialCharactersChange = passwordHandlers
|
||||
.onPasswordToggleSpecialCharactersChange,
|
||||
enabled = passwordTypeState.specialCharsEnabled,
|
||||
)
|
||||
}
|
||||
PasswordCapitalLettersToggleItem(
|
||||
useCapitals = passwordTypeState.useCapitals,
|
||||
onPasswordToggleCapitalLettersChange = passwordHandlers
|
||||
.onPasswordToggleCapitalLettersChange,
|
||||
enabled = passwordTypeState.capitalsEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
PasswordLowercaseLettersToggleItem(
|
||||
useLowercase = passwordTypeState.useLowercase,
|
||||
onPasswordToggleLowercaseLettersChange = passwordHandlers
|
||||
.onPasswordToggleLowercaseLettersChange,
|
||||
enabled = passwordTypeState.lowercaseEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
PasswordNumbersToggleItem(
|
||||
useNumbers = passwordTypeState.useNumbers,
|
||||
onPasswordToggleNumbersChange = passwordHandlers.onPasswordToggleNumbersChange,
|
||||
enabled = passwordTypeState.numbersEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
PasswordSpecialCharactersToggleItem(
|
||||
useSpecialChars = passwordTypeState.useSpecialChars,
|
||||
onPasswordToggleSpecialCharactersChange = passwordHandlers
|
||||
.onPasswordToggleSpecialCharactersChange,
|
||||
enabled = passwordTypeState.specialCharsEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
PasswordAvoidAmbiguousCharsToggleItem(
|
||||
avoidAmbiguousChars = passwordTypeState.avoidAmbiguousChars,
|
||||
onPasswordToggleAvoidAmbiguousCharsChange = passwordHandlers
|
||||
.onPasswordToggleAvoidAmbiguousCharsChange,
|
||||
enabled = passwordTypeState.ambiguousCharsEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
@@ -514,25 +526,20 @@ private fun ColumnScope.PasswordTypeContent(
|
||||
onPasswordMinNumbersCounterChange = passwordHandlers.onPasswordMinNumbersCounterChange,
|
||||
maxValue = max(passwordTypeState.maxNumbersAllowed, passwordTypeState.minNumbersAllowed),
|
||||
minValue = passwordTypeState.minNumbersAllowed,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
PasswordMinSpecialCharactersCounterItem(
|
||||
minSpecial = passwordTypeState.minSpecial,
|
||||
onPasswordMinSpecialCharactersChange = passwordHandlers
|
||||
.onPasswordMinSpecialCharactersChange,
|
||||
maxValue = max(passwordTypeState.maxSpecialAllowed, passwordTypeState.minSpecialAllowed),
|
||||
minValue = passwordTypeState.minSpecialAllowed,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
PasswordAvoidAmbiguousCharsToggleItem(
|
||||
avoidAmbiguousChars = passwordTypeState.avoidAmbiguousChars,
|
||||
onPasswordToggleAvoidAmbiguousCharsChange = passwordHandlers
|
||||
.onPasswordToggleAvoidAmbiguousCharsChange,
|
||||
enabled = passwordTypeState.ambiguousCharsEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -549,10 +556,8 @@ private fun PasswordCapitalLettersToggleItem(
|
||||
isChecked = useCapitals,
|
||||
onCheckedChange = onPasswordToggleCapitalLettersChange,
|
||||
enabled = enabled,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("UppercaseAtoZToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = modifier.testTag(tag = "UppercaseAtoZToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -569,10 +574,8 @@ private fun PasswordLowercaseLettersToggleItem(
|
||||
isChecked = useLowercase,
|
||||
onCheckedChange = onPasswordToggleLowercaseLettersChange,
|
||||
enabled = enabled,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("LowercaseAtoZToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = modifier.testTag(tag = "LowercaseAtoZToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -589,10 +592,8 @@ private fun PasswordNumbersToggleItem(
|
||||
isChecked = useNumbers,
|
||||
onCheckedChange = onPasswordToggleNumbersChange,
|
||||
enabled = enabled,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("NumbersZeroToNineToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = modifier.testTag(tag = "NumbersZeroToNineToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -609,10 +610,8 @@ private fun PasswordSpecialCharactersToggleItem(
|
||||
isChecked = useSpecialChars,
|
||||
onCheckedChange = onPasswordToggleSpecialCharactersChange,
|
||||
enabled = enabled,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("SpecialCharactersToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = modifier.testTag(tag = "SpecialCharactersToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -629,9 +628,8 @@ private fun PasswordMinNumbersCounterItem(
|
||||
value = minNumbers.coerceIn(minValue, maxValue),
|
||||
range = minValue..maxValue,
|
||||
onValueChange = onPasswordMinNumbersCounterChange,
|
||||
modifier = modifier
|
||||
.testTag("MinNumberValueLabel")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = modifier.testTag(tag = "MinNumberValueLabel"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -648,9 +646,8 @@ private fun PasswordMinSpecialCharactersCounterItem(
|
||||
value = minSpecial.coerceIn(minValue, maxValue),
|
||||
range = minValue..maxValue,
|
||||
onValueChange = onPasswordMinSpecialCharactersChange,
|
||||
modifier = modifier
|
||||
.testTag("MinSpecialValueLabel")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier.testTag(tag = "MinSpecialValueLabel"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -666,10 +663,8 @@ private fun PasswordAvoidAmbiguousCharsToggleItem(
|
||||
isChecked = avoidAmbiguousChars,
|
||||
enabled = enabled,
|
||||
onCheckedChange = onPasswordToggleAvoidAmbiguousCharsChange,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("AvoidAmbiguousCharsToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier.testTag(tag = "AvoidAmbiguousCharsToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -682,13 +677,14 @@ private fun ColumnScope.PassphraseTypeContent(
|
||||
passphraseTypeState: GeneratorState.MainType.Passphrase,
|
||||
passphraseHandlers: PassphraseHandlers,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
PassphraseNumWordsCounterItem(
|
||||
numWords = passphraseTypeState.numWords,
|
||||
onPassphraseNumWordsCounterChange = passphraseHandlers.onPassphraseNumWordsCounterChange,
|
||||
minValue = passphraseTypeState.minNumWords,
|
||||
maxValue = passphraseTypeState.maxNumWords,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -696,26 +692,31 @@ private fun ColumnScope.PassphraseTypeContent(
|
||||
PassphraseWordSeparatorInputItem(
|
||||
wordSeparator = passphraseTypeState.wordSeparator,
|
||||
onPassphraseWordSeparatorChange = passphraseHandlers.onPassphraseWordSeparatorChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
PassphraseCapitalizeToggleItem(
|
||||
capitalize = passphraseTypeState.capitalize,
|
||||
onPassphraseCapitalizeToggleChange = passphraseHandlers
|
||||
.onPassphraseCapitalizeToggleChange,
|
||||
enabled = passphraseTypeState.capitalizeEnabled,
|
||||
)
|
||||
PassphraseIncludeNumberToggleItem(
|
||||
includeNumber = passphraseTypeState.includeNumber,
|
||||
onPassphraseIncludeNumberToggleChange = passphraseHandlers
|
||||
.onPassphraseIncludeNumberToggleChange,
|
||||
enabled = passphraseTypeState.includeNumberEnabled,
|
||||
)
|
||||
}
|
||||
PassphraseCapitalizeToggleItem(
|
||||
capitalize = passphraseTypeState.capitalize,
|
||||
onPassphraseCapitalizeToggleChange = passphraseHandlers
|
||||
.onPassphraseCapitalizeToggleChange,
|
||||
enabled = passphraseTypeState.capitalizeEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
PassphraseIncludeNumberToggleItem(
|
||||
includeNumber = passphraseTypeState.includeNumber,
|
||||
onPassphraseIncludeNumberToggleChange = passphraseHandlers
|
||||
.onPassphraseIncludeNumberToggleChange,
|
||||
enabled = passphraseTypeState.includeNumberEnabled,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -726,17 +727,14 @@ private fun PassphraseNumWordsCounterItem(
|
||||
minValue: Int = PASSPHRASE_MIN_NUMBER_OF_WORDS,
|
||||
maxValue: Int = PASSPHRASE_MAX_NUMBER_OF_WORDS,
|
||||
) {
|
||||
val coercedNumWords = numWords.coerceIn(minValue, maxValue)
|
||||
|
||||
BitwardenStepper(
|
||||
label = stringResource(id = R.string.number_of_words),
|
||||
value = coercedNumWords,
|
||||
value = numWords.coerceIn(minimumValue = minValue, maximumValue = maxValue),
|
||||
range = minValue..maxValue,
|
||||
onValueChange = onPassphraseNumWordsCounterChange,
|
||||
stepperActionsTestTag = "NumberOfWordsStepper",
|
||||
modifier = modifier
|
||||
.testTag("NumberOfWordsLabel")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier.testTag(tag = "NumberOfWordsLabel"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -758,10 +756,9 @@ private fun PassphraseWordSeparatorInputItem(
|
||||
onPassphraseWordSeparatorChange(char)
|
||||
}
|
||||
},
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Full,
|
||||
textFieldTestTag = "WordSeparatorEntry",
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -777,10 +774,8 @@ private fun PassphraseCapitalizeToggleItem(
|
||||
isChecked = capitalize,
|
||||
onCheckedChange = onPassphraseCapitalizeToggleChange,
|
||||
enabled = enabled,
|
||||
modifier = modifier
|
||||
.testTag("CapitalizePassphraseToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = modifier.testTag(tag = "CapitalizePassphraseToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -796,10 +791,8 @@ private fun PassphraseIncludeNumberToggleItem(
|
||||
isChecked = includeNumber,
|
||||
enabled = enabled,
|
||||
onCheckedChange = onPassphraseIncludeNumberToggleChange,
|
||||
modifier = modifier
|
||||
.testTag("IncludeNumbersToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier.testTag(tag = "IncludeNumbersToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -817,7 +810,14 @@ private fun ColumnScope.UsernameTypeItems(
|
||||
catchAllEmailHandlers: CatchAllEmailHandlers,
|
||||
randomWordHandlers: RandomWordHandlers,
|
||||
) {
|
||||
UsernameOptionsItem(usernameState, onSubStateOptionClicked, usernameTypeHandlers)
|
||||
UsernameOptionsItem(
|
||||
currentSubState = usernameState,
|
||||
onSubStateOptionClicked = onSubStateOptionClicked,
|
||||
usernameTypeHandlers = usernameTypeHandlers,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
when (val selectedType = usernameState.selectedType) {
|
||||
is GeneratorState.MainType.Username.UsernameType.PlusAddressedEmail -> {
|
||||
@@ -876,10 +876,8 @@ private fun UsernameOptionsItem(
|
||||
onClick = usernameTypeHandlers.onUsernameTooltipClicked,
|
||||
contentDescription = stringResource(id = R.string.learn_more),
|
||||
),
|
||||
modifier = modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth()
|
||||
.testTag("UsernameTypePicker"),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier.testTag(tag = "UsernameTypePicker"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -898,20 +896,23 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
ServiceTypeOptionsItem(
|
||||
currentSubState = usernameTypeState,
|
||||
onSubStateOptionClicked = forwardedEmailAliasHandlers.onServiceChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
when (usernameTypeState.selectedServiceType) {
|
||||
|
||||
is ServiceType.AddyIo -> {
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.api_access_token),
|
||||
value = usernameTypeState.selectedServiceType.apiAccessToken,
|
||||
onValueChange = forwardedEmailAliasHandlers.onAddyIoAccessTokenTextChange,
|
||||
showPasswordTestTag = "ShowForwardedEmailApiSecretButton",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("ForwardedEmailApiSecretEntry")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -922,10 +923,11 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
label = stringResource(id = R.string.domain_name_required_parenthesis),
|
||||
value = usernameTypeState.selectedServiceType.domainName,
|
||||
onValueChange = forwardedEmailAliasHandlers.onAddyIoDomainNameTextChange,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
textFieldTestTag = "AnonAddyDomainNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -935,8 +937,9 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
value = usernameTypeState.selectedServiceType.apiKey,
|
||||
onValueChange = forwardedEmailAliasHandlers.onDuckDuckGoApiKeyTextChange,
|
||||
showPasswordTestTag = "ShowForwardedEmailApiSecretButton",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("ForwardedEmailApiSecretEntry")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -948,8 +951,9 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
value = usernameTypeState.selectedServiceType.apiKey,
|
||||
onValueChange = forwardedEmailAliasHandlers.onFastMailApiKeyTextChange,
|
||||
showPasswordTestTag = "ShowForwardedEmailApiSecretButton",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("ForwardedEmailApiSecretEntry")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -961,8 +965,9 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
value = usernameTypeState.selectedServiceType.apiAccessToken,
|
||||
onValueChange = forwardedEmailAliasHandlers.onFirefoxRelayAccessTokenTextChange,
|
||||
showPasswordTestTag = "ShowForwardedEmailApiSecretButton",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("ForwardedEmailApiSecretEntry")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -974,8 +979,9 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
value = usernameTypeState.selectedServiceType.apiKey,
|
||||
onValueChange = forwardedEmailAliasHandlers.onForwardEmailApiKeyTextChange,
|
||||
showPasswordTestTag = "ShowForwardedEmailApiSecretButton",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("ForwardedEmailApiSecretEntry")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -986,10 +992,11 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
label = stringResource(id = R.string.domain_name_required_parenthesis),
|
||||
value = usernameTypeState.selectedServiceType.domainName,
|
||||
onValueChange = forwardedEmailAliasHandlers.onForwardEmailDomainNameTextChange,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
textFieldTestTag = "ForwardedEmailDomainNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -999,8 +1006,9 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
value = usernameTypeState.selectedServiceType.apiKey,
|
||||
onValueChange = forwardedEmailAliasHandlers.onSimpleLoginApiKeyTextChange,
|
||||
showPasswordTestTag = "ShowForwardedEmailApiSecretButton",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("ForwardedEmailApiSecretEntry")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -1013,8 +1021,9 @@ private fun ColumnScope.ForwardedEmailAliasTypeContent(
|
||||
value = obfuscatedTextField,
|
||||
onValueChange = { obfuscatedTextField = it },
|
||||
showPasswordTestTag = "ShowForwardedEmailApiSecretButton",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("ForwardedEmailApiSecretEntry")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -1042,10 +1051,8 @@ private fun ServiceTypeOptionsItem(
|
||||
optionsWithStrings.entries.first { it.value == selectedOption }.key
|
||||
onSubStateOptionClicked(selectedOptionId)
|
||||
},
|
||||
modifier = modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.testTag("ServiceTypePicker")
|
||||
.fillMaxWidth(),
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier.testTag(tag = "ServiceTypePicker"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1061,6 +1068,9 @@ private fun ColumnScope.PlusAddressedEmailTypeContent(
|
||||
PlusAddressedEmailTextInputItem(
|
||||
email = usernameTypeState.email,
|
||||
onPlusAddressedEmailTextChange = plusAddressedEmailHandlers.onEmailChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1074,10 +1084,9 @@ private fun PlusAddressedEmailTextInputItem(
|
||||
label = stringResource(id = R.string.email_required_parenthesis),
|
||||
value = email,
|
||||
onValueChange = onPlusAddressedEmailTextChange,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "PlusAddressedEmailEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1095,6 +1104,9 @@ private fun ColumnScope.CatchAllEmailTypeContent(
|
||||
CatchAllEmailTextInputItem(
|
||||
domain = usernameTypeState.domainName,
|
||||
onDomainTextChange = catchAllEmailHandlers.onDomainChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1108,10 +1120,9 @@ private fun CatchAllEmailTextInputItem(
|
||||
label = stringResource(id = R.string.domain_name_required_parenthesis),
|
||||
value = domain,
|
||||
onValueChange = onDomainTextChange,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "CatchAllEmailDomainEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1124,16 +1135,22 @@ private fun ColumnScope.RandomWordTypeContent(
|
||||
randomWordTypeState: GeneratorState.MainType.Username.UsernameType.RandomWord,
|
||||
randomWordHandlers: RandomWordHandlers,
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
RandomWordCapitalizeToggleItem(
|
||||
capitalize = randomWordTypeState.capitalize,
|
||||
onRandomWordCapitalizeToggleChange = randomWordHandlers.onCapitalizeChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
|
||||
RandomWordIncludeNumberToggleItem(
|
||||
includeNumber = randomWordTypeState.includeNumber,
|
||||
onRandomWordIncludeNumberToggleChange = randomWordHandlers.onIncludeNumberChange,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1147,10 +1164,8 @@ private fun RandomWordCapitalizeToggleItem(
|
||||
label = stringResource(id = R.string.capitalize),
|
||||
isChecked = capitalize,
|
||||
onCheckedChange = onRandomWordCapitalizeToggleChange,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("CapitalizeRandomWordUsernameToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = modifier.testTag(tag = "CapitalizeRandomWordUsernameToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1164,10 +1179,8 @@ private fun RandomWordIncludeNumberToggleItem(
|
||||
label = stringResource(id = R.string.include_number),
|
||||
isChecked = includeNumber,
|
||||
onCheckedChange = onRandomWordIncludeNumberToggleChange,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("IncludeNumberRandomWordUsernameToggle")
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier.testTag(tag = "IncludeNumberRandomWordUsernameToggle"),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+12
-3
@@ -18,9 +18,11 @@ import androidx.compose.ui.res.stringResource
|
||||
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.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.withLineBreaksAtWidth
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.withVisualTransformation
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.nonLetterColorVisualTransformation
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
@@ -37,12 +39,18 @@ fun PasswordHistoryListItem(
|
||||
label: String,
|
||||
supportingLabel: String,
|
||||
onCopyClick: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.then(modifier),
|
||||
modifier = modifier
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
bottom = 12.dp,
|
||||
start = 16.dp,
|
||||
end = 4.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
@@ -90,6 +98,7 @@ private fun PasswordHistoryListItem_preview() {
|
||||
label = "8gr6uY8CLYQwzr#",
|
||||
supportingLabel = "8/24/2023 11:07 AM",
|
||||
onCopyClick = {},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
|
||||
+13
-8
@@ -7,11 +7,11 @@ import androidx.compose.foundation.layout.Column
|
||||
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.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
@@ -30,10 +30,11 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.appbar.action.OverflowMenuItemData
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.indicator.BitwardenCircularProgressIndicator
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -72,8 +73,8 @@ fun PasswordHistoryScreen(
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.password_history),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = rememberVectorPainter(id = R.drawable.ic_close),
|
||||
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||
navigationIcon = rememberVectorPainter(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(PasswordHistoryAction.CloseClick) }
|
||||
},
|
||||
@@ -155,19 +156,23 @@ private fun PasswordHistoryContent(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
items(state.passwords) { password ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
itemsIndexed(state.passwords) { index, password ->
|
||||
PasswordHistoryListItem(
|
||||
label = password.password,
|
||||
supportingLabel = password.date,
|
||||
onCopyClick = { onPasswordCopyClick(password) },
|
||||
cardStyle = state.passwords.toListItemCardStyle(index = index),
|
||||
modifier = Modifier
|
||||
.testTag("GeneratedPasswordRow")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
BitwardenHorizontalDivider(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,19 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenGroupItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.tools.feature.send.handlers.SendHandlers
|
||||
@@ -36,14 +39,18 @@ fun SendContent(
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
if (policyDisablesSend) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
if (policyDisablesSend) {
|
||||
item {
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = R.string.send_disabled_warning),
|
||||
modifier = Modifier
|
||||
.testTag("SendOptionsPolicyInEffectLabel")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +60,10 @@ fun SendContent(
|
||||
supportingLabel = SEND_TYPES_COUNT.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -63,10 +72,11 @@ fun SendContent(
|
||||
supportingLabel = state.textTypeCount.toString(),
|
||||
startIcon = rememberVectorPainter(id = R.drawable.ic_file_text),
|
||||
onClick = sendHandlers.onTextTypeClick,
|
||||
cardStyle = CardStyle.Top(dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("SendTextFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,10 +86,11 @@ fun SendContent(
|
||||
supportingLabel = state.fileTypeCount.toString(),
|
||||
startIcon = rememberVectorPainter(id = R.drawable.ic_file),
|
||||
onClick = sendHandlers.onFileTypeClick,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("SendFileFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,11 +101,13 @@ fun SendContent(
|
||||
supportingLabel = state.sendItems.size.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
items(state.sendItems) {
|
||||
itemsIndexed(state.sendItems) { index, it ->
|
||||
SendListItem(
|
||||
startIcon = IconData.Local(it.type.iconRes),
|
||||
label = it.name,
|
||||
@@ -111,14 +124,12 @@ fun SendContent(
|
||||
} else {
|
||||
null
|
||||
},
|
||||
cardStyle = state
|
||||
.sendItems
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.testTag("SendCell")
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
// There is some built-in padding to the menu button that makes up
|
||||
// the visual difference here.
|
||||
end = 12.dp,
|
||||
)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenListItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.SelectionItemData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconRes
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
|
||||
@@ -37,6 +38,7 @@ import kotlinx.collections.immutable.toPersistentList
|
||||
* @param onDeleteClick The lambda to be invoked when the delete option is clicked from the menu.
|
||||
* @param onRemovePasswordClick The lambda to be invoked when the remove password option is clicked
|
||||
* from the menu, if `null` the remove password button is not displayed.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param modifier An optional [Modifier] for this Composable, defaulting to an empty Modifier.
|
||||
* This allows the caller to specify things like padding, size, etc.
|
||||
*/
|
||||
@@ -54,6 +56,7 @@ fun SendListItem(
|
||||
onShareClick: () -> Unit,
|
||||
onDeleteClick: () -> Unit,
|
||||
onRemovePasswordClick: (() -> Unit)?,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowDeleteConfirmationDialog by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -98,6 +101,7 @@ fun SendListItem(
|
||||
.filter { showMoreOptions }
|
||||
.toPersistentList(),
|
||||
optionsTestTag = "Options",
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
if (shouldShowDeleteConfirmationDialog) {
|
||||
@@ -116,7 +120,7 @@ fun SendListItem(
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SendListItem_preview() {
|
||||
BitwardenTheme {
|
||||
@@ -132,6 +136,7 @@ private fun SendListItem_preview() {
|
||||
onShareClick = {},
|
||||
onDeleteClick = {},
|
||||
onRemovePasswordClick = null,
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
+176
-152
@@ -8,8 +8,10 @@ import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
@@ -21,6 +23,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.minimumInteractiveComponentSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -34,13 +37,20 @@ 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.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.stepper.BitwardenStepper
|
||||
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.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
@@ -69,12 +79,12 @@ fun AddSendContent(
|
||||
Column(
|
||||
modifier = modifier.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
if (policyDisablesSend) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = R.string.send_disabled_warning),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth()
|
||||
.testTag("SendPolicyInEffectLabel"),
|
||||
)
|
||||
@@ -86,35 +96,36 @@ fun AddSendContent(
|
||||
text = stringResource(id = R.string.send_options_policy_in_effect),
|
||||
modifier = Modifier
|
||||
.testTag(tag = "SendPolicyInEffectLabel")
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
BitwardenTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
label = stringResource(id = R.string.name),
|
||||
hint = stringResource(id = R.string.name_info),
|
||||
supportingText = stringResource(id = R.string.name_info),
|
||||
readOnly = policyDisablesSend,
|
||||
value = state.common.name,
|
||||
onValueChange = addSendHandlers.onNamChange,
|
||||
textFieldTestTag = "SendNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
when (val type = state.selectedType) {
|
||||
is AddSendState.ViewState.Content.SendType.File -> {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.file),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
if (isShared) {
|
||||
Text(
|
||||
text = type.name.orEmpty(),
|
||||
@@ -122,6 +133,7 @@ fun AddSendContent(
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -131,22 +143,31 @@ fun AddSendContent(
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
} else if (isAddMode) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.testTag(tag = "SendCurrentFileNameLabel")
|
||||
.align(Alignment.CenterHorizontally),
|
||||
text = type.name ?: stringResource(id = R.string.no_file_chosen),
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
type.name?.let {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = CardStyle.Full)
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
)
|
||||
.testTag(tag = "SendCurrentFileNameLabel"),
|
||||
text = it,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
style = BitwardenTheme.typography.bodyLarge,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
BitwardenOutlinedButton(
|
||||
label = stringResource(id = R.string.choose_file),
|
||||
onClick = {
|
||||
@Suppress("MaxLineLength")
|
||||
if (permissionsManager.checkPermission(Manifest.permission.CAMERA)) {
|
||||
addSendHandlers.onChooseFileClick(true)
|
||||
} else {
|
||||
@@ -158,31 +179,30 @@ fun AddSendContent(
|
||||
modifier = Modifier
|
||||
.testTag(tag = "SendChooseFileButton")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.max_file_size),
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.type_file_info),
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
} else {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin()
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = CardStyle.Full)
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = type.name.orEmpty(),
|
||||
@@ -193,7 +213,7 @@ fun AddSendContent(
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = type.displaySize.orEmpty(),
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
style = BitwardenTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
@@ -201,33 +221,35 @@ fun AddSendContent(
|
||||
}
|
||||
|
||||
is AddSendState.ViewState.Content.SendType.Text -> {
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
label = stringResource(id = R.string.text),
|
||||
hint = stringResource(id = R.string.type_text_info),
|
||||
supportingText = stringResource(id = R.string.type_text_info),
|
||||
readOnly = policyDisablesSend,
|
||||
value = type.input,
|
||||
singleLine = false,
|
||||
onValueChange = addSendHandlers.onTextChange,
|
||||
textFieldTestTag = "SendTextContentEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
modifier = Modifier
|
||||
.testTag(tag = "SendHideTextByDefaultToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
label = stringResource(id = R.string.hide_text_by_default),
|
||||
isChecked = type.isHideByDefaultChecked,
|
||||
onCheckedChange = addSendHandlers.onIsHideByDefaultToggle,
|
||||
readOnly = policyDisablesSend,
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
AddSendOptions(
|
||||
state = state,
|
||||
sendRestrictionPolicy = policyDisablesSend,
|
||||
@@ -235,7 +257,7 @@ fun AddSendContent(
|
||||
addSendHandlers = addSendHandlers,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -271,12 +293,14 @@ private fun AddSendOptions(
|
||||
},
|
||||
onClick = { isExpanded = !isExpanded },
|
||||
)
|
||||
.padding(16.dp)
|
||||
.minimumInteractiveComponentSize()
|
||||
.padding(top = 16.dp, bottom = 8.dp)
|
||||
.standardHorizontalMargin()
|
||||
.semantics(mergeDescendants = true) {},
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.options),
|
||||
text = stringResource(id = R.string.additional_options),
|
||||
color = BitwardenTheme.colorScheme.text.interaction,
|
||||
style = BitwardenTheme.typography.labelLarge,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
@@ -306,7 +330,7 @@ private fun AddSendOptions(
|
||||
modifier = Modifier
|
||||
.testTag("SendDeletionOptionsPicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
@@ -318,7 +342,7 @@ private fun AddSendOptions(
|
||||
modifier = Modifier
|
||||
.testTag("SendExpirationOptionsPicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.expirationDate,
|
||||
@@ -326,174 +350,174 @@ private fun AddSendOptions(
|
||||
isEnabled = !sendRestrictionPolicy,
|
||||
)
|
||||
} else {
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.deletion_date),
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AddSendCustomDateChooser(
|
||||
modifier = Modifier
|
||||
.testTag("SendCustomDeletionDatePicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateLabel = stringResource(id = R.string.deletion_date),
|
||||
timeLabel = stringResource(id = R.string.deletion_time),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
isEnabled = !sendRestrictionPolicy,
|
||||
onDateSelect = { addSendHandlers.onDeletionDateChange(requireNotNull(it)) },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.deletion_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.expiration_date),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AddSendCustomDateChooser(
|
||||
modifier = Modifier
|
||||
.testTag("SendCustomExpirationDatePicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
dateLabel = stringResource(id = R.string.expiration_date),
|
||||
timeLabel = stringResource(id = R.string.expiration_time),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.expirationDate,
|
||||
onDateSelect = addSendHandlers.onExpirationDateChange,
|
||||
isEnabled = !sendRestrictionPolicy,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
.standardHorizontalMargin()
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = CardStyle.Full)
|
||||
.cardPadding(cardStyle = CardStyle.Full, vertical = 0.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.expiration_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = R.string.clear),
|
||||
onClick = addSendHandlers.onClearExpirationDateClick,
|
||||
isEnabled = state.common.expirationDate != null && !sendRestrictionPolicy,
|
||||
AddSendCustomDateChooser(
|
||||
modifier = Modifier
|
||||
.testTag("SendClearExpirationDateButton")
|
||||
.wrapContentWidth(),
|
||||
.testTag("SendCustomDeletionDatePicker")
|
||||
.fillMaxWidth(),
|
||||
dateLabel = stringResource(id = R.string.deletion_date),
|
||||
timeLabel = stringResource(id = R.string.deletion_time),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.deletionDate,
|
||||
isEnabled = !sendRestrictionPolicy,
|
||||
onDateSelect = { addSendHandlers.onDeletionDateChange(requireNotNull(it)) },
|
||||
)
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.deletion_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = CardStyle.Full)
|
||||
.cardPadding(cardStyle = CardStyle.Full, vertical = 0.dp),
|
||||
) {
|
||||
AddSendCustomDateChooser(
|
||||
modifier = Modifier
|
||||
.testTag("SendCustomExpirationDatePicker")
|
||||
.fillMaxWidth(),
|
||||
dateLabel = stringResource(id = R.string.expiration_date),
|
||||
timeLabel = stringResource(id = R.string.expiration_time),
|
||||
dateFormatPattern = state.common.dateFormatPattern,
|
||||
timeFormatPattern = state.common.timeFormatPattern,
|
||||
currentZonedDateTime = state.common.expirationDate,
|
||||
onDateSelect = addSendHandlers.onExpirationDateChange,
|
||||
isEnabled = !sendRestrictionPolicy,
|
||||
)
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.expiration_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.weight(weight = 1f),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(width = 4.dp))
|
||||
BitwardenClickableText(
|
||||
label = stringResource(id = R.string.clear),
|
||||
onClick = addSendHandlers.onClearExpirationDateClick,
|
||||
isEnabled = state.common.expirationDate != null &&
|
||||
!sendRestrictionPolicy,
|
||||
style = BitwardenTheme.typography.labelLarge,
|
||||
innerPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp),
|
||||
modifier = Modifier
|
||||
.testTag("SendClearExpirationDateButton")
|
||||
.wrapContentWidth(),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenStepper(
|
||||
label = stringResource(id = R.string.maximum_access_count),
|
||||
supportingTextContent = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.maximum_access_count_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
state.common.currentAccessCount.takeUnless { isAddMode }?.let {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = R.string.current_access_count
|
||||
.asText()
|
||||
.concat(": ".asText(), it.toString().asText())
|
||||
.invoke(),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
},
|
||||
value = state.common.maxAccessCount,
|
||||
onValueChange = addSendHandlers.onMaxAccessCountChange,
|
||||
isDecrementEnabled = state.common.maxAccessCount != null && !sendRestrictionPolicy,
|
||||
isIncrementEnabled = !sendRestrictionPolicy,
|
||||
range = 0..Int.MAX_VALUE,
|
||||
textFieldReadOnly = sendRestrictionPolicy,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("SendMaxAccessCountEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.maximum_access_count_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
)
|
||||
if (!isAddMode) {
|
||||
state.common.currentAccessCount?.let {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.current_access_count) + ":",
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = it.toString(),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.new_password),
|
||||
hint = stringResource(id = R.string.password_info),
|
||||
supportingText = stringResource(id = R.string.password_info),
|
||||
readOnly = sendRestrictionPolicy,
|
||||
value = state.common.passwordInput,
|
||||
onValueChange = addSendHandlers.onPasswordChange,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("SendNewPasswordEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.notes),
|
||||
hint = stringResource(id = R.string.notes_info),
|
||||
supportingText = stringResource(id = R.string.notes_info),
|
||||
readOnly = sendRestrictionPolicy,
|
||||
value = state.common.noteInput,
|
||||
singleLine = false,
|
||||
onValueChange = addSendHandlers.onNoteChange,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
modifier = Modifier
|
||||
.testTag("SendHideEmailSwitch")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
label = stringResource(id = R.string.hide_email),
|
||||
isChecked = state.common.isHideEmailChecked,
|
||||
onCheckedChange = addSendHandlers.onHideEmailToggle,
|
||||
readOnly = sendRestrictionPolicy,
|
||||
enabled = state.common.isHideEmailChecked || state.common.isHideEmailAddressEnabled,
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
modifier = Modifier
|
||||
.testTag("SendDeactivateSwitch")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
label = stringResource(id = R.string.disable_send),
|
||||
isChecked = state.common.isDeactivateChecked,
|
||||
onCheckedChange = addSendHandlers.onDeactivateSendToggle,
|
||||
readOnly = sendRestrictionPolicy,
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+15
-5
@@ -2,7 +2,9 @@ package com.x8bit.bitwarden.ui.tools.feature.send.addsend
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -18,7 +20,11 @@ import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.time.ZonedDateTime
|
||||
@@ -43,7 +49,10 @@ fun SendDeletionDateChooser(
|
||||
val options = DeletionOptions.entries.associateWith { it.text() }
|
||||
var selectedOption: DeletionOptions by rememberSaveable { mutableStateOf(defaultOption) }
|
||||
Column(
|
||||
modifier = modifier,
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = CardStyle.Full)
|
||||
.cardPadding(cardStyle = CardStyle.Full, vertical = 0.dp),
|
||||
) {
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.deletion_date),
|
||||
@@ -59,11 +68,11 @@ fun SendDeletionDateChooser(
|
||||
)
|
||||
}
|
||||
},
|
||||
insets = PaddingValues(top = 6.dp, bottom = 4.dp),
|
||||
)
|
||||
|
||||
AnimatedVisibility(visible = selectedOption == DeletionOptions.CUSTOM) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
AddSendCustomDateChooser(
|
||||
dateLabel = stringResource(id = R.string.deletion_date),
|
||||
timeLabel = stringResource(id = R.string.deletion_time),
|
||||
@@ -75,8 +84,8 @@ fun SendDeletionDateChooser(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.deletion_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
@@ -85,6 +94,7 @@ fun SendDeletionDateChooser(
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-5
@@ -2,7 +2,9 @@ package com.x8bit.bitwarden.ui.tools.feature.send.addsend
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -18,7 +20,11 @@ import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.time.ZonedDateTime
|
||||
@@ -43,7 +49,10 @@ fun SendExpirationDateChooser(
|
||||
val options = ExpirationOptions.entries.associateWith { it.text() }
|
||||
var selectedOption: ExpirationOptions by rememberSaveable { mutableStateOf(defaultOption) }
|
||||
Column(
|
||||
modifier = modifier,
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = CardStyle.Full)
|
||||
.cardPadding(cardStyle = CardStyle.Full, vertical = 0.dp),
|
||||
) {
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.expiration_date),
|
||||
@@ -61,11 +70,11 @@ fun SendExpirationDateChooser(
|
||||
)
|
||||
}
|
||||
},
|
||||
insets = PaddingValues(top = 6.dp, bottom = 4.dp),
|
||||
)
|
||||
|
||||
AnimatedVisibility(visible = selectedOption == ExpirationOptions.CUSTOM) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
AddSendCustomDateChooser(
|
||||
dateLabel = stringResource(id = R.string.expiration_date),
|
||||
timeLabel = stringResource(id = R.string.expiration_time),
|
||||
@@ -77,8 +86,8 @@ fun SendExpirationDateChooser(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
BitwardenHorizontalDivider(modifier = Modifier.padding(start = 16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.expiration_date_info),
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
@@ -87,6 +96,7 @@ fun SendExpirationDateChooser(
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-7
@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -14,6 +14,8 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
@@ -30,39 +32,40 @@ fun LazyListScope.collectionItemsSelector(
|
||||
|
||||
if (isCollectionsTitleVisible) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.collections),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (collectionList?.isNotEmpty() == true) {
|
||||
items(collectionList) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
itemsIndexed(collectionList) { index, it ->
|
||||
BitwardenSwitch(
|
||||
label = it.name,
|
||||
isChecked = it.isSelected,
|
||||
onCheckedChange = { _ ->
|
||||
onCollectionSelect(it)
|
||||
},
|
||||
cardStyle = collectionList.toListItemCardStyle(index = index),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("CollectionItemCell")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.no_collections_to_list),
|
||||
|
||||
+60
-43
@@ -17,11 +17,13 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.components.collectionItemsSelector
|
||||
@@ -51,10 +53,11 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = commonHandlers.onNameTextChange,
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
@@ -63,14 +66,14 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
label = stringResource(id = R.string.cardholder_name),
|
||||
value = cardState.cardHolderName,
|
||||
onValueChange = cardHandlers.onCardHolderNameTextChange,
|
||||
textFieldTestTag = "CardholderNameEntry",
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "CardholderNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
var showNumber by rememberSaveable { mutableStateOf(value = false) }
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.number),
|
||||
@@ -81,16 +84,16 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
showNumber = !showNumber
|
||||
cardHandlers.onNumberVisibilityChange(showNumber)
|
||||
},
|
||||
showPasswordTestTag = "ShowCardNumberButton",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("CardNumberEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
showPasswordTestTag = "ShowCardNumberButton",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
val resources = LocalContext.current.resources
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.brand),
|
||||
options = VaultCardBrand
|
||||
@@ -105,14 +108,15 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
.first { it.longName.toString(resources) == selectedString },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("CardBrandPicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
val resources = LocalContext.current.resources
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.expiration_month),
|
||||
options = VaultCardExpirationMonth
|
||||
@@ -127,26 +131,27 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
.first { it.value.toString(resources) == selectedString },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("CardExpirationMonthPicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.expiration_year),
|
||||
value = cardState.expirationYear,
|
||||
onValueChange = cardHandlers.onExpirationYearTextChange,
|
||||
keyboardType = KeyboardType.Number,
|
||||
textFieldTestTag = "CardExpirationYearEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "CardExpirationYearEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
var showSecurityCode by rememberSaveable { mutableStateOf(value = false) }
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.security_code),
|
||||
@@ -158,24 +163,26 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
cardHandlers.onSecurityCodeVisibilityChange(showSecurityCode)
|
||||
},
|
||||
keyboardType = KeyboardType.NumberPassword,
|
||||
showPasswordTestTag = "CardShowSecurityCodeButton",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("CardSecurityCodeEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
showPasswordTestTag = "CardShowSecurityCodeButton",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.miscellaneous),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.folder),
|
||||
options = commonState
|
||||
@@ -190,37 +197,39 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
.first { it.name == selectedFolderName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("FolderPicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(
|
||||
id = R.string.favorite,
|
||||
),
|
||||
isChecked = commonState.favorite,
|
||||
onCheckedChange = commonHandlers.onToggleFavorite,
|
||||
cardStyle = if (commonState.isUnlockWithPasswordEnabled) {
|
||||
CardStyle.Top()
|
||||
} else {
|
||||
CardStyle.Full
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ItemFavoriteToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
if (commonState.isUnlockWithPasswordEnabled) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.password_prompt),
|
||||
isChecked = commonState.masterPasswordReprompt,
|
||||
onCheckedChange = commonHandlers.onToggleMasterPasswordReprompt,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle,
|
||||
@@ -231,53 +240,58 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
singleLine = false,
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = commonState.notes,
|
||||
onValueChange = commonHandlers.onNotesTextChange,
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
items(commonState.customFieldData) { customItem ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
VaultAddEditCustomField(
|
||||
customField = customItem,
|
||||
onCustomFieldValueChange = commonHandlers.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonHandlers.onCustomFieldActionSelect,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
supportedLinkedTypes = persistentListOf(
|
||||
VaultLinkedFieldType.CARDHOLDER_NAME,
|
||||
VaultLinkedFieldType.EXPIRATION_MONTH,
|
||||
@@ -287,6 +301,10 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
VaultLinkedFieldType.NUMBER,
|
||||
),
|
||||
onHiddenVisibilityChanged = commonHandlers.onHiddenFieldVisibilityChange,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -296,23 +314,24 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
onFinishNamingClick = commonHandlers.onAddNewCustomFieldClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
if (isAddItemMode && commonState.hasOrganizations) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.ownership),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.who_owns_this_item),
|
||||
options = commonState
|
||||
@@ -327,10 +346,11 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
.first { it.name == selectedOwnerName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("ItemOwnershipPicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
if (commonState.selectedOwnerId != null) {
|
||||
@@ -340,7 +360,4 @@ fun LazyListScope.vaultAddEditCardItems(
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
+60
-39
@@ -1,7 +1,12 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.addedit
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -11,16 +16,21 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTextEntryDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenBasicDialogRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.row.BitwardenRowOfActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -33,9 +43,10 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
* @param customField The field that is to be displayed.
|
||||
* @param onCustomFieldValueChange Invoked when the user changes the value.
|
||||
* @param onCustomFieldAction Invoked when the user chooses an action.
|
||||
* @param onHiddenVisibilityChanged Emits when the visibility of a hidden custom field changes.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param modifier Modifier for the UI elements.
|
||||
* @param supportedLinkedTypes The supported linked types for the vault item.
|
||||
* @param onHiddenVisibilityChanged Emits when the visibility of a hidden custom field changes.
|
||||
*/
|
||||
@Composable
|
||||
@Suppress("LongMethod")
|
||||
@@ -43,9 +54,10 @@ fun VaultAddEditCustomField(
|
||||
customField: VaultAddEditState.Custom,
|
||||
onCustomFieldValueChange: (VaultAddEditState.Custom) -> Unit,
|
||||
onCustomFieldAction: (CustomFieldAction, VaultAddEditState.Custom) -> Unit,
|
||||
onHiddenVisibilityChanged: (Boolean) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
|
||||
onHiddenVisibilityChanged: (Boolean) -> Unit,
|
||||
) {
|
||||
var shouldShowChooserDialog by remember { mutableStateOf(false) }
|
||||
var shouldShowEditDialog by remember { mutableStateOf(false) }
|
||||
@@ -85,6 +97,7 @@ fun VaultAddEditCustomField(
|
||||
value = customField.value,
|
||||
onValueChanged = { onCustomFieldValueChange(customField.copy(value = it)) },
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -98,6 +111,7 @@ fun VaultAddEditCustomField(
|
||||
},
|
||||
onVisibilityChanged = onHiddenVisibilityChanged,
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -112,6 +126,7 @@ fun VaultAddEditCustomField(
|
||||
onCustomFieldValueChange(customField.copy(vaultLinkedFieldType = it))
|
||||
},
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -123,6 +138,7 @@ fun VaultAddEditCustomField(
|
||||
value = customField.value,
|
||||
onValueChanged = { onCustomFieldValueChange(customField.copy(value = it)) },
|
||||
onEditValue = { shouldShowChooserDialog = true },
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -138,24 +154,33 @@ private fun CustomFieldBoolean(
|
||||
value: Boolean,
|
||||
onValueChanged: (Boolean) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.semantics(mergeDescendants = true) {}
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(color = BitwardenTheme.colorScheme.background.pressed),
|
||||
onClick = { onValueChanged(!value) },
|
||||
)
|
||||
.cardPadding(cardStyle = cardStyle, end = 4.dp, top = 6.dp, bottom = 6.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
BitwardenSwitch(
|
||||
label = label,
|
||||
isChecked = value,
|
||||
onCheckedChange = onValueChanged,
|
||||
onCheckedChange = null,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
|
||||
BitwardenRowOfActions(
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_cog,
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
onClick = onEditValue,
|
||||
@@ -175,6 +200,7 @@ private fun CustomFieldHiddenField(
|
||||
onValueChanged: (String) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
onVisibilityChanged: (Boolean) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowPassword by remember { mutableStateOf(value = false) }
|
||||
@@ -188,14 +214,15 @@ private fun CustomFieldHiddenField(
|
||||
onVisibilityChanged(shouldShowPassword)
|
||||
},
|
||||
singleLine = true,
|
||||
modifier = modifier,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_cog,
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -208,6 +235,7 @@ private fun CustomFieldTextField(
|
||||
value: String,
|
||||
onValueChanged: (String) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTextFieldWithActions(
|
||||
@@ -215,14 +243,15 @@ private fun CustomFieldTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChanged,
|
||||
singleLine = true,
|
||||
modifier = modifier,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_cog,
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -235,41 +264,33 @@ private fun CustomFieldLinkedField(
|
||||
selectedOption: VaultLinkedFieldType,
|
||||
onValueChanged: (VaultLinkedFieldType) -> Unit,
|
||||
onEditValue: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
supportedLinkedTypes: ImmutableList<VaultLinkedFieldType> = persistentListOf(),
|
||||
) {
|
||||
val possibleTypesWithStrings = supportedLinkedTypes.associateWith { it.label.invoke() }
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.semantics(mergeDescendants = true) {}
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
BitwardenMultiSelectButton(
|
||||
label = label,
|
||||
options = supportedLinkedTypes.map { it.label.invoke() }.toImmutableList(),
|
||||
selectedOption = selectedOption.label.invoke(),
|
||||
onOptionSelected = { selectedType ->
|
||||
possibleTypesWithStrings.forEach {
|
||||
if (it.value == selectedType) {
|
||||
onValueChanged(it.key)
|
||||
}
|
||||
BitwardenMultiSelectButton(
|
||||
modifier = modifier,
|
||||
label = label,
|
||||
options = supportedLinkedTypes.map { it.label.invoke() }.toImmutableList(),
|
||||
selectedOption = selectedOption.label.invoke(),
|
||||
onOptionSelected = { selectedType ->
|
||||
possibleTypesWithStrings.forEach {
|
||||
if (it.value == selectedType) {
|
||||
onValueChanged(it.key)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
|
||||
BitwardenRowOfActions(
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
vectorIconRes = R.drawable.ic_cog,
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_cog,
|
||||
contentDescription = stringResource(id = R.string.edit),
|
||||
onClick = onEditValue,
|
||||
)
|
||||
},
|
||||
actionsPadding = PaddingValues(end = 4.dp),
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+95
-80
@@ -13,10 +13,12 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.components.collectionItemsSelector
|
||||
@@ -44,10 +46,11 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = commonTypeHandlers.onNameTextChange,
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
@@ -58,224 +61,225 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
modifier = Modifier
|
||||
.testTag("IdentityTitlePicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.first_name),
|
||||
value = identityState.firstName,
|
||||
onValueChange = identityItemTypeHandlers.onFirstNameTextChange,
|
||||
textFieldTestTag = "IdentityFirstNameEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityFirstNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.middle_name),
|
||||
value = identityState.middleName,
|
||||
onValueChange = identityItemTypeHandlers.onMiddleNameTextChange,
|
||||
textFieldTestTag = "IdentityMiddleNameEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityMiddleNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.last_name),
|
||||
value = identityState.lastName,
|
||||
onValueChange = identityItemTypeHandlers.onLastNameTextChange,
|
||||
textFieldTestTag = "IdentityLastNameEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityLastNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.username),
|
||||
value = identityState.username,
|
||||
onValueChange = identityItemTypeHandlers.onUsernameTextChange,
|
||||
textFieldTestTag = "IdentityUsernameEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityUsernameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.company),
|
||||
value = identityState.company,
|
||||
onValueChange = identityItemTypeHandlers.onCompanyTextChange,
|
||||
textFieldTestTag = "IdentityCompanyEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityCompanyEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.ssn),
|
||||
value = identityState.ssn,
|
||||
onValueChange = identityItemTypeHandlers.onSsnTextChange,
|
||||
textFieldTestTag = "IdentitySsnEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentitySsnEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.passport_number),
|
||||
value = identityState.passportNumber,
|
||||
onValueChange = identityItemTypeHandlers.onPassportNumberTextChange,
|
||||
textFieldTestTag = "IdentityPassportNumberEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityPassportNumberEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.license_number),
|
||||
value = identityState.licenseNumber,
|
||||
onValueChange = identityItemTypeHandlers.onLicenseNumberTextChange,
|
||||
textFieldTestTag = "IdentityLicenseNumberEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityLicenseNumberEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.email),
|
||||
value = identityState.email,
|
||||
onValueChange = identityItemTypeHandlers.onEmailTextChange,
|
||||
textFieldTestTag = "IdentityEmailEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityEmailEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.phone),
|
||||
value = identityState.phone,
|
||||
onValueChange = identityItemTypeHandlers.onPhoneTextChange,
|
||||
textFieldTestTag = "IdentityPhoneEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityPhoneEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.address1),
|
||||
value = identityState.address1,
|
||||
onValueChange = identityItemTypeHandlers.onAddress1TextChange,
|
||||
textFieldTestTag = "IdentityAddressOneEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityAddressOneEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.address2),
|
||||
value = identityState.address2,
|
||||
onValueChange = identityItemTypeHandlers.onAddress2TextChange,
|
||||
textFieldTestTag = "IdentityAddressTwoEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityAddressTwoEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.address3),
|
||||
value = identityState.address3,
|
||||
onValueChange = identityItemTypeHandlers.onAddress3TextChange,
|
||||
textFieldTestTag = "IdentityAddressThreeEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityAddressThreeEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.city_town),
|
||||
value = identityState.city,
|
||||
onValueChange = identityItemTypeHandlers.onCityTextChange,
|
||||
textFieldTestTag = "IdentityCityEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityCityEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.state_province),
|
||||
value = identityState.state,
|
||||
onValueChange = identityItemTypeHandlers.onStateTextChange,
|
||||
textFieldTestTag = "IdentityStateEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityStateEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.zip_postal_code),
|
||||
value = identityState.zip,
|
||||
onValueChange = identityItemTypeHandlers.onZipTextChange,
|
||||
textFieldTestTag = "IdentityPostalCodeEntry",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityPostalCodeEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.country),
|
||||
value = identityState.country,
|
||||
onValueChange = identityItemTypeHandlers.onCountryTextChange,
|
||||
textFieldTestTag = "IdentityCountryEntry",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "IdentityCountryEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.miscellaneous),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.folder),
|
||||
options = commonState
|
||||
@@ -290,38 +294,40 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
.first { it.name == selectedFolderName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("FolderPicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(
|
||||
id = R.string.favorite,
|
||||
),
|
||||
isChecked = commonState.favorite,
|
||||
onCheckedChange = commonTypeHandlers.onToggleFavorite,
|
||||
cardStyle = if (commonState.isUnlockWithPasswordEnabled) {
|
||||
CardStyle.Top()
|
||||
} else {
|
||||
CardStyle.Full
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ItemFavoriteToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
if (commonState.isUnlockWithPasswordEnabled) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.password_prompt),
|
||||
isChecked = commonState.masterPasswordReprompt,
|
||||
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle,
|
||||
@@ -332,53 +338,58 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
singleLine = false,
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = commonState.notes,
|
||||
onValueChange = commonTypeHandlers.onNotesTextChange,
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
items(commonState.customFieldData) { customItem ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
VaultAddEditCustomField(
|
||||
customField = customItem,
|
||||
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
supportedLinkedTypes = persistentListOf(
|
||||
VaultLinkedFieldType.TITLE,
|
||||
VaultLinkedFieldType.MIDDLE_NAME,
|
||||
@@ -401,6 +412,10 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
VaultLinkedFieldType.FULL_NAME,
|
||||
),
|
||||
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -410,23 +425,24 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
onFinishNamingClick = commonTypeHandlers.onAddNewCustomFieldClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
if (isAddItemMode && commonState.hasOrganizations) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.ownership),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.who_owns_this_item),
|
||||
options = commonState
|
||||
@@ -441,10 +457,11 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
.first { it.name == selectedOwnerName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("ItemOwnershipPicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
if (commonState.selectedOwnerId != null) {
|
||||
@@ -454,9 +471,6 @@ fun LazyListScope.vaultAddEditIdentityItems(
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -480,6 +494,7 @@ private fun TitleMultiSelectButton(
|
||||
.first { it.value.toString(resources) == selectedString },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
+12
-5
@@ -13,9 +13,11 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCardTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
|
||||
@@ -57,12 +59,13 @@ fun VaultAddEditContent(
|
||||
)
|
||||
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
if (state.isIndividualVaultDisabled && isAddItemMode) {
|
||||
if (state.isIndividualVaultDisabled && isAddItemMode) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(R.string.personal_ownership_policy_in_effect),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("PersonalOwnershipPolicyLabel")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
@@ -70,23 +73,25 @@ fun VaultAddEditContent(
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.item_information),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
if (isAddItemMode) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
TypeOptionsItem(
|
||||
entries = typeOptions,
|
||||
itemType = state.type,
|
||||
onTypeOptionClicked = onTypeOptionClicked,
|
||||
modifier = Modifier
|
||||
.testTag("ItemTypePicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -148,6 +153,7 @@ fun VaultAddEditContent(
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -173,6 +179,7 @@ private fun TypeOptionsItem(
|
||||
.key
|
||||
onTypeOptionClicked(selectedOptionId)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
+84
-58
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -18,9 +19,10 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenHiddenPasswordField
|
||||
@@ -28,6 +30,7 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordFieldWi
|
||||
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.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
@@ -51,15 +54,15 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
onTotpSetupClick: () -> Unit,
|
||||
) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = commonActionHandler.onNameTextChange,
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,15 +71,20 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
UsernameRow(
|
||||
username = loginState.username,
|
||||
loginItemTypeHandlers = loginItemTypeHandlers,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
PasswordRow(
|
||||
password = loginState.password,
|
||||
canViewPassword = loginState.canViewPassword,
|
||||
loginItemTypeHandlers = loginItemTypeHandlers,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -85,23 +93,25 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
PasskeyField(
|
||||
creationDateTime = creationDateTime,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
canRemovePasskey = loginState.canRemovePasskey,
|
||||
loginItemTypeHandlers = loginItemTypeHandlers,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.authenticator_key),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
if (loginState.totp != null) {
|
||||
@@ -109,7 +119,7 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
BitwardenTextFieldWithActions(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
label = stringResource(id = R.string.totp),
|
||||
value = loginState.totp,
|
||||
trailingIconContent = {
|
||||
@@ -123,20 +133,21 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_totp),
|
||||
onClick = {
|
||||
loginItemTypeHandlers.onCopyTotpKeyClick(loginState.totp)
|
||||
},
|
||||
)
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_camera,
|
||||
contentDescription = stringResource(id = R.string.camera),
|
||||
onClick = onTotpSetupClick,
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "LoginTotpEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -149,27 +160,32 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
modifier = Modifier
|
||||
.testTag("SetupTotpButton")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.ur_is),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
items(loginState.uriList) { uriItem ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
itemsIndexed(loginState.uriList) { index, uriItem ->
|
||||
VaultAddEditUriItem(
|
||||
uriItem = uriItem,
|
||||
onUriValueChange = loginItemTypeHandlers.onUriValueChange,
|
||||
onUriItemRemoved = loginItemTypeHandlers.onRemoveUriClick,
|
||||
cardStyle = loginState.uriList.toListItemCardStyle(index = index),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -181,22 +197,23 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
modifier = Modifier
|
||||
.testTag("LoginAddNewUriButton")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.miscellaneous),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.folder),
|
||||
options = commonState
|
||||
@@ -211,37 +228,39 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
.first { it.name == selectedFolderName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("FolderPicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(
|
||||
id = R.string.favorite,
|
||||
),
|
||||
isChecked = commonState.favorite,
|
||||
onCheckedChange = commonActionHandler.onToggleFavorite,
|
||||
cardStyle = if (commonState.isUnlockWithPasswordEnabled) {
|
||||
CardStyle.Top()
|
||||
} else {
|
||||
CardStyle.Full
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ItemFavoriteToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
if (commonState.isUnlockWithPasswordEnabled) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.password_prompt),
|
||||
isChecked = commonState.masterPasswordReprompt,
|
||||
onCheckedChange = commonActionHandler.onToggleMasterPasswordReprompt,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle_small,
|
||||
@@ -252,58 +271,67 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
singleLine = false,
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = commonState.notes,
|
||||
onValueChange = commonActionHandler.onNotesTextChange,
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
items(commonState.customFieldData) { customItem ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
VaultAddEditCustomField(
|
||||
customField = customItem,
|
||||
onCustomFieldValueChange = commonActionHandler.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonActionHandler.onCustomFieldActionSelect,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
supportedLinkedTypes = persistentListOf(
|
||||
VaultLinkedFieldType.PASSWORD,
|
||||
VaultLinkedFieldType.USERNAME,
|
||||
),
|
||||
onHiddenVisibilityChanged = commonActionHandler.onHiddenFieldVisibilityChange,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -313,23 +341,24 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
onFinishNamingClick = commonActionHandler.onAddNewCustomFieldClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
if (isAddItemMode && commonState.hasOrganizations) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.ownership),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.who_owns_this_item),
|
||||
options = commonState
|
||||
@@ -344,10 +373,11 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
.first { it.name == selectedOwnerName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("ItemOwnershipPicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -358,16 +388,13 @@ fun LazyListScope.vaultAddEditLoginItems(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UsernameRow(
|
||||
username: String,
|
||||
loginItemTypeHandlers: VaultAddEditLoginTypeHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@@ -376,7 +403,7 @@ private fun UsernameRow(
|
||||
value = username,
|
||||
onValueChange = loginItemTypeHandlers.onUsernameTextChange,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_generate,
|
||||
contentDescription = stringResource(id = R.string.generate_username),
|
||||
onClick = {
|
||||
@@ -389,9 +416,9 @@ private fun UsernameRow(
|
||||
modifier = Modifier.testTag(tag = "GenerateUsernameButton"),
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "LoginUsernameEntry",
|
||||
cardStyle = CardStyle.Top(dividerPadding = 0.dp),
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
if (shouldShowDialog) {
|
||||
@@ -423,6 +450,7 @@ private fun PasswordRow(
|
||||
password: String,
|
||||
canViewPassword: Boolean,
|
||||
loginItemTypeHandlers: VaultAddEditLoginTypeHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@@ -439,17 +467,16 @@ private fun PasswordRow(
|
||||
},
|
||||
showPasswordTestTag = "ViewPasswordButton",
|
||||
passwordFieldTestTag = "LoginPasswordEntry",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier,
|
||||
) {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_check_mark,
|
||||
contentDescription = stringResource(id = R.string.check_password),
|
||||
onClick = loginItemTypeHandlers.onPasswordCheckerClick,
|
||||
modifier = Modifier.testTag(tag = "CheckPasswordButton"),
|
||||
)
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_generate,
|
||||
contentDescription = stringResource(id = R.string.generate_password),
|
||||
onClick = {
|
||||
@@ -488,10 +515,8 @@ private fun PasswordRow(
|
||||
BitwardenHiddenPasswordField(
|
||||
label = stringResource(id = R.string.password),
|
||||
value = password,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.testTag("LoginPasswordEntry"),
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = modifier.testTag("LoginPasswordEntry"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -509,10 +534,9 @@ private fun PasskeyField(
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
modifier = modifier,
|
||||
actions = {
|
||||
if (canRemovePasskey) {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_minus,
|
||||
contentDescription = stringResource(id = R.string.remove_passkey),
|
||||
onClick = loginItemTypeHandlers.onClearFido2CredentialClick,
|
||||
@@ -520,5 +544,7 @@ private fun PasskeyField(
|
||||
)
|
||||
}
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
+41
-29
@@ -11,10 +11,12 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.components.collectionItemsSelector
|
||||
@@ -38,25 +40,27 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = commonTypeHandlers.onNameTextChange,
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.miscellaneous),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.folder),
|
||||
options = commonState
|
||||
@@ -71,36 +75,38 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
||||
.first { it.name == selectedFolderName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("FolderPicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.favorite),
|
||||
isChecked = commonState.favorite,
|
||||
onCheckedChange = commonTypeHandlers.onToggleFavorite,
|
||||
cardStyle = if (commonState.isUnlockWithPasswordEnabled) {
|
||||
CardStyle.Top()
|
||||
} else {
|
||||
CardStyle.Full
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ItemFavoriteToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
if (commonState.isUnlockWithPasswordEnabled) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.password_prompt),
|
||||
isChecked = commonState.masterPasswordReprompt,
|
||||
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle,
|
||||
@@ -111,53 +117,61 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
singleLine = false,
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = commonState.notes,
|
||||
onValueChange = commonTypeHandlers.onNotesTextChange,
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
items(commonState.customFieldData) { customItem ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
VaultAddEditCustomField(
|
||||
customField = customItem,
|
||||
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
|
||||
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -172,23 +186,24 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
if (isAddItemMode && commonState.hasOrganizations) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.ownership),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.who_owns_this_item),
|
||||
options = commonState
|
||||
@@ -203,10 +218,11 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
||||
.first { it.name == selectedOwnerName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("ItemOwnershipPicker")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -217,8 +233,4 @@ fun LazyListScope.vaultAddEditSecureNotesItems(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
+39
-24
@@ -20,6 +20,7 @@ import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectB
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
|
||||
@@ -44,10 +45,11 @@ fun LazyListScope.vaultAddEditSshKeyItems(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = commonTypeHandlers.onNameTextChange,
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,15 +60,15 @@ fun LazyListScope.vaultAddEditSshKeyItems(
|
||||
value = sshKeyState.publicKey,
|
||||
readOnly = true,
|
||||
onValueChange = { },
|
||||
textFieldTestTag = "PublicKeyEntry",
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
textFieldTestTag = "PublicKeyEntry",
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenPasswordField(
|
||||
label = stringResource(id = R.string.private_key),
|
||||
value = sshKeyState.privateKey,
|
||||
@@ -75,6 +77,7 @@ fun LazyListScope.vaultAddEditSshKeyItems(
|
||||
showPassword = sshKeyState.showPrivateKey,
|
||||
showPasswordChange = { sshKeyTypeHandlers.onPrivateKeyVisibilityChange(it) },
|
||||
showPasswordTestTag = "ViewPrivateKeyButton",
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("PrivateKeyEntry")
|
||||
.fillMaxWidth()
|
||||
@@ -83,31 +86,32 @@ fun LazyListScope.vaultAddEditSshKeyItems(
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.fingerprint),
|
||||
value = sshKeyState.fingerprint,
|
||||
readOnly = true,
|
||||
onValueChange = { /* no-op */ },
|
||||
textFieldTestTag = "FingerprintEntry",
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
textFieldTestTag = "FingerprintEntry",
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.miscellaneous),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.folder),
|
||||
options = commonState
|
||||
@@ -122,37 +126,39 @@ fun LazyListScope.vaultAddEditSshKeyItems(
|
||||
.first { it.name == selectedFolderName },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("FolderPicker")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(
|
||||
id = R.string.favorite,
|
||||
),
|
||||
isChecked = commonState.favorite,
|
||||
onCheckedChange = commonTypeHandlers.onToggleFavorite,
|
||||
cardStyle = if (commonState.isUnlockWithPasswordEnabled) {
|
||||
CardStyle.Top()
|
||||
} else {
|
||||
CardStyle.Full
|
||||
},
|
||||
modifier = Modifier
|
||||
.testTag("ItemFavoriteToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
if (commonState.isUnlockWithPasswordEnabled) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
BitwardenSwitch(
|
||||
label = stringResource(id = R.string.password_prompt),
|
||||
isChecked = commonState.masterPasswordReprompt,
|
||||
onCheckedChange = commonTypeHandlers.onToggleMasterPasswordReprompt,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_question_circle_small,
|
||||
@@ -163,54 +169,63 @@ fun LazyListScope.vaultAddEditSshKeyItems(
|
||||
contentColor = BitwardenTheme.colorScheme.icon.secondary,
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("MasterPasswordRepromptToggle")
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
singleLine = false,
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = commonState.notes,
|
||||
onValueChange = commonTypeHandlers.onNotesTextChange,
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNotesEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
items(commonState.customFieldData) { customItem ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
VaultAddEditCustomField(
|
||||
customField = customItem,
|
||||
onCustomFieldValueChange = commonTypeHandlers.onCustomFieldValueChange,
|
||||
onCustomFieldAction = commonTypeHandlers.onCustomFieldActionSelect,
|
||||
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
onHiddenVisibilityChanged = commonTypeHandlers.onHiddenFieldVisibilityChange,
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -225,7 +240,7 @@ fun LazyListScope.vaultAddEditSshKeyItems(
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -1,6 +1,5 @@
|
||||
package com.x8bit.bitwarden.ui.vault.feature.addedit
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -9,13 +8,13 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
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.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenSelectionDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenBasicDialogRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.row.BitwardenSelectionRow
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriMatchDisplayType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toDisplayMatchType
|
||||
@@ -30,6 +29,8 @@ fun VaultAddEditUriItem(
|
||||
uriItem: UriItem,
|
||||
onUriItemRemoved: (UriItem) -> Unit,
|
||||
onUriValueChange: (UriItem) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowOptionsDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var shouldShowMatchDialog by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -39,16 +40,16 @@ fun VaultAddEditUriItem(
|
||||
value = uriItem.uri.orEmpty(),
|
||||
onValueChange = { onUriValueChange(uriItem.copy(uri = it)) },
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_cog,
|
||||
contentDescription = stringResource(id = R.string.options),
|
||||
onClick = { shouldShowOptionsDialog = true },
|
||||
modifier = Modifier.testTag(tag = "LoginUriOptionsButton"),
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "LoginUriEntry",
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
if (shouldShowOptionsDialog) {
|
||||
|
||||
+27
-16
@@ -9,7 +9,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -24,11 +24,15 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.feature.attachments.handlers.AttachmentsHandlers
|
||||
|
||||
@@ -54,35 +58,40 @@ fun AttachmentsContent(
|
||||
modifier = Modifier
|
||||
.testTag("NoAttachmentsLabel")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
} else {
|
||||
items(viewState.attachments) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
itemsIndexed(viewState.attachments) { index, it ->
|
||||
AttachmentListEntry(
|
||||
attachmentItem = it,
|
||||
onDeleteClick = attachmentsHandlers.onDeleteClick,
|
||||
cardStyle = viewState.attachments.toListItemCardStyle(index = index),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("AttachmentList"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(36.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.add_new_attachment),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = viewState
|
||||
.newAttachment
|
||||
@@ -93,7 +102,7 @@ fun AttachmentsContent(
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("SelectedFileNameLabel"),
|
||||
)
|
||||
}
|
||||
@@ -105,7 +114,7 @@ fun AttachmentsContent(
|
||||
onClick = attachmentsHandlers.onChooseFileClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.testTag("AttachmentSelectFileButton"),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
@@ -115,11 +124,13 @@ fun AttachmentsContent(
|
||||
style = BitwardenTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
@@ -129,6 +140,7 @@ fun AttachmentsContent(
|
||||
private fun AttachmentListEntry(
|
||||
attachmentItem: AttachmentsState.AttachmentItem,
|
||||
onDeleteClick: (attachmentId: String) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowDeleteDialog by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -148,12 +160,11 @@ private fun AttachmentListEntry(
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.bottomDivider(paddingStart = 16.dp)
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.testTag("AttachmentRow")
|
||||
.padding(vertical = 8.dp)
|
||||
.then(modifier),
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, start = 16.dp)
|
||||
.testTag("AttachmentRow"),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
|
||||
+7
-5
@@ -3,7 +3,6 @@ package com.x8bit.bitwarden.ui.vault.feature.item
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Text
|
||||
@@ -19,10 +18,12 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.bottomDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardBackground
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.cardPadding
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -33,15 +34,16 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
fun AttachmentItemContent(
|
||||
attachmentItem: VaultItemState.ViewState.Content.Common.AttachmentItem,
|
||||
onAttachmentDownloadClick: (VaultItemState.ViewState.Content.Common.AttachmentItem) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var shouldShowPremiumWarningDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var shouldShowSizeWarningDialog by rememberSaveable { mutableStateOf(false) }
|
||||
Row(
|
||||
modifier = modifier
|
||||
.bottomDivider()
|
||||
.defaultMinSize(minHeight = 56.dp)
|
||||
.padding(vertical = 8.dp)
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.cardBackground(cardStyle = cardStyle)
|
||||
.cardPadding(cardStyle = cardStyle, start = 16.dp)
|
||||
.testTag("CipherAttachment"),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
||||
+70
-35
@@ -7,17 +7,21 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
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.platform.components.model.CardStyle
|
||||
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.model.VaultCardBrand
|
||||
@@ -37,46 +41,53 @@ fun VaultItemCardContent(
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.item_information),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "CardItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "CardItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
cardState.cardholderName?.let { cardholderName ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.cardholder_name),
|
||||
value = cardholderName,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "CardholderNameEntry",
|
||||
cardStyle = cardState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = cardState.propertyList.indexOf(element = cardholderName),
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "CardholderNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
cardState.number?.let { numberData ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenPasswordFieldWithActions(
|
||||
label = stringResource(id = R.string.number),
|
||||
value = numberData.number,
|
||||
@@ -86,7 +97,7 @@ fun VaultItemCardContent(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_number),
|
||||
onClick = vaultCardItemTypeHandlers.onCopyNumberClick,
|
||||
@@ -95,50 +106,62 @@ fun VaultItemCardContent(
|
||||
},
|
||||
passwordFieldTestTag = "CardNumberEntry",
|
||||
showPasswordTestTag = "CardViewNumberButton",
|
||||
cardStyle = cardState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = cardState.propertyList.indexOf(element = numberData),
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (cardState.brand != null && cardState.brand != VaultCardBrand.SELECT) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.brand),
|
||||
value = cardState.brand.shortName(),
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "CardBrandEntry",
|
||||
cardStyle = cardState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = cardState.propertyList.indexOf(element = cardState.brand),
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "CardBrandEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
cardState.expiration?.let { expiration ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.expiration),
|
||||
value = expiration,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "CardExpirationEntry",
|
||||
cardStyle = cardState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = cardState.propertyList.indexOf(element = expiration),
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "CardExpirationEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
cardState.securityCode?.let { securityCodeData ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenPasswordFieldWithActions(
|
||||
label = stringResource(id = R.string.security_code),
|
||||
value = securityCodeData.code,
|
||||
@@ -148,29 +171,35 @@ fun VaultItemCardContent(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_security_code),
|
||||
onClick = vaultCardItemTypeHandlers.onCopySecurityCodeClick,
|
||||
modifier = Modifier.testTag(tag = "CardCopySecurityCodeButton"),
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
showPasswordTestTag = "CardViewSecurityCodeButton",
|
||||
passwordFieldTestTag = "CardSecurityCodeEntry",
|
||||
cardStyle = cardState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = cardState.propertyList.indexOf(element = securityCodeData),
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.notes?.let { notes ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -181,7 +210,7 @@ fun VaultItemCardContent(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
@@ -189,69 +218,75 @@ fun VaultItemCardContent(
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.customFields.takeUnless { it.isEmpty() }?.let { customFields ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
items(customFields) { customField ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
CustomField(
|
||||
customField = customField,
|
||||
onCopyCustomHiddenField = vaultCommonItemTypeHandlers.onCopyCustomHiddenField,
|
||||
onCopyCustomTextField = vaultCommonItemTypeHandlers.onCopyCustomTextField,
|
||||
onShowHiddenFieldClick = vaultCommonItemTypeHandlers.onShowHiddenFieldClick,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.attachments.takeUnless { it?.isEmpty() == true }?.let { attachments ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.attachments),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(attachments) { attachmentItem ->
|
||||
itemsIndexed(attachments) { index, attachmentItem ->
|
||||
AttachmentItemContent(
|
||||
modifier = Modifier
|
||||
.testTag("CipherAttachment")
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
attachmentItem = attachmentItem,
|
||||
onAttachmentDownloadClick =
|
||||
vaultCommonItemTypeHandlers.onAttachmentDownloadClick,
|
||||
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
|
||||
.onAttachmentDownloadClick,
|
||||
cardStyle = attachments.toListItemCardStyle(index = index),
|
||||
)
|
||||
}
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
VaultItemUpdateText(
|
||||
header = "${stringResource(id = R.string.date_updated)}: ",
|
||||
text = commonState.lastUpdated,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp),
|
||||
)
|
||||
}
|
||||
item {
|
||||
|
||||
+42
-20
@@ -4,10 +4,12 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
|
||||
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.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
@@ -15,13 +17,17 @@ import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
/**
|
||||
* Custom Field UI common for all item types.
|
||||
*/
|
||||
@Suppress("LongMethod", "MaxLineLength")
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun CustomField(
|
||||
customField: VaultItemState.ViewState.Content.Common.Custom,
|
||||
onCopyCustomHiddenField: (String) -> Unit,
|
||||
onCopyCustomTextField: (String) -> Unit,
|
||||
onShowHiddenFieldClick: (VaultItemState.ViewState.Content.Common.Custom.HiddenField, Boolean) -> Unit,
|
||||
onShowHiddenFieldClick: (
|
||||
VaultItemState.ViewState.Content.Common.Custom.HiddenField,
|
||||
Boolean,
|
||||
) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (customField) {
|
||||
@@ -31,30 +37,44 @@ fun CustomField(
|
||||
isChecked = customField.value,
|
||||
readOnly = true,
|
||||
onCheckedChange = { },
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
is VaultItemState.ViewState.Content.Common.Custom.HiddenField -> {
|
||||
BitwardenPasswordFieldWithActions(
|
||||
label = customField.name,
|
||||
value = customField.value,
|
||||
showPasswordChange = { onShowHiddenFieldClick(customField, it) },
|
||||
showPassword = customField.isVisible,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
modifier = modifier,
|
||||
actions = {
|
||||
if (customField.isCopyable) {
|
||||
BitwardenTonalIconButton(
|
||||
if (customField.isCopyable) {
|
||||
BitwardenPasswordFieldWithActions(
|
||||
label = customField.name,
|
||||
value = customField.value,
|
||||
showPasswordChange = { onShowHiddenFieldClick(customField, it) },
|
||||
showPassword = customField.isVisible,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy),
|
||||
onClick = { onCopyCustomHiddenField(customField.value) },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
BitwardenPasswordField(
|
||||
label = customField.name,
|
||||
value = customField.value,
|
||||
showPasswordChange = { onShowHiddenFieldClick(customField, it) },
|
||||
showPassword = customField.isVisible,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is VaultItemState.ViewState.Content.Common.Custom.LinkedField -> {
|
||||
@@ -68,6 +88,7 @@ fun CustomField(
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -79,16 +100,17 @@ fun CustomField(
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
modifier = modifier,
|
||||
actions = {
|
||||
if (customField.isCopyable) {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy),
|
||||
onClick = { onCopyCustomTextField(customField.value) },
|
||||
)
|
||||
}
|
||||
},
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+99
-37
@@ -7,16 +7,20 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
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.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultIdentityItemTypeHandlers
|
||||
|
||||
@@ -34,30 +38,33 @@ fun VaultItemIdentityContent(
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.item_information),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
identityState.identityName?.let { identityName ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.identity_name),
|
||||
value = identityName,
|
||||
@@ -65,15 +72,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityNameEntry",
|
||||
copyActionTestTag = "IdentityCopyNameButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyIdentityNameClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = identityName),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.username?.let { username ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.username),
|
||||
value = username,
|
||||
@@ -81,15 +93,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityUsernameEntry",
|
||||
copyActionTestTag = "IdentityCopyUsernameButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyUsernameClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = username),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.company?.let { company ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.company),
|
||||
value = company,
|
||||
@@ -97,15 +114,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityCompanyEntry",
|
||||
copyActionTestTag = "IdentityCopyCompanyButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyCompanyClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = company),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.ssn?.let { ssn ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.ssn),
|
||||
value = ssn,
|
||||
@@ -113,15 +135,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentitySsnEntry",
|
||||
copyActionTestTag = "IdentityCopySsnButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopySsnClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = ssn),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.passportNumber?.let { passportNumber ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.passport_number),
|
||||
value = passportNumber,
|
||||
@@ -129,15 +156,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityPassportNumberEntry",
|
||||
copyActionTestTag = "IdentityCopyPassportNumberButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyPassportNumberClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = passportNumber),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.licenseNumber?.let { licenseNumber ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.license_number),
|
||||
value = licenseNumber,
|
||||
@@ -145,15 +177,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityLicenseNumberEntry",
|
||||
copyActionTestTag = "IdentityCopyLicenseNumberButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyLicenseNumberClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = licenseNumber),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.email?.let { email ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.email),
|
||||
value = email,
|
||||
@@ -161,15 +198,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityEmailEntry",
|
||||
copyActionTestTag = "IdentityCopyEmailButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyEmailClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = email),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.phone?.let { phone ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.phone),
|
||||
value = phone,
|
||||
@@ -177,15 +219,20 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityPhoneEntry",
|
||||
copyActionTestTag = "IdentityCopyPhoneButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyPhoneClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = phone),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
identityState.address?.let { address ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.address),
|
||||
value = address,
|
||||
@@ -193,19 +240,26 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "IdentityAddressEntry",
|
||||
copyActionTestTag = "IdentityCopyAddressButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyAddressClick,
|
||||
cardStyle = identityState
|
||||
.propertyList
|
||||
.toListItemCardStyle(
|
||||
index = identityState.propertyList.indexOf(element = address),
|
||||
dividerPadding = 0.dp,
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
commonState.notes?.let { notes ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -216,69 +270,75 @@ fun VaultItemIdentityContent(
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
copyActionTestTag = "CipherNotesCopyButton",
|
||||
onCopyClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.customFields.takeUnless { it.isEmpty() }?.let { customFields ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
items(customFields) { customField ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
CustomField(
|
||||
customField = customField,
|
||||
onCopyCustomHiddenField = vaultCommonItemTypeHandlers.onCopyCustomHiddenField,
|
||||
onCopyCustomTextField = vaultCommonItemTypeHandlers.onCopyCustomTextField,
|
||||
onShowHiddenFieldClick = vaultCommonItemTypeHandlers.onShowHiddenFieldClick,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.attachments.takeUnless { it?.isEmpty() == true }?.let { attachments ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.attachments),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(attachments) { attachmentItem ->
|
||||
itemsIndexed(attachments) { index, attachmentItem ->
|
||||
AttachmentItemContent(
|
||||
modifier = Modifier
|
||||
.testTag("CipherAttachment")
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
attachmentItem = attachmentItem,
|
||||
onAttachmentDownloadClick =
|
||||
vaultCommonItemTypeHandlers.onAttachmentDownloadClick,
|
||||
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
|
||||
.onAttachmentDownloadClick,
|
||||
cardStyle = attachments.toListItemCardStyle(index = index),
|
||||
)
|
||||
}
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
VaultItemUpdateText(
|
||||
header = "${stringResource(id = R.string.date_updated)}: ",
|
||||
text = commonState.lastUpdated,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp),
|
||||
)
|
||||
}
|
||||
item {
|
||||
@@ -296,6 +356,7 @@ private fun IdentityCopyField(
|
||||
textFieldTestTag: String,
|
||||
copyActionTestTag: String,
|
||||
onCopyClick: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTextFieldWithActions(
|
||||
@@ -305,14 +366,15 @@ private fun IdentityCopyField(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = copyContentDescription,
|
||||
onClick = onCopyClick,
|
||||
modifier = Modifier.testTag(tag = copyActionTestTag),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
+112
-65
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -17,13 +18,16 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenHiddenPasswordField
|
||||
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.platform.components.indicator.BitwardenCircularCountdownIndicator
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultLoginItemTypeHandlers
|
||||
@@ -34,7 +38,7 @@ private const val AUTH_CODE_SPACING_INTERVAL = 3
|
||||
/**
|
||||
* The top level content UI state for the [VaultItemScreen] when viewing a Login cipher.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun VaultItemLoginContent(
|
||||
commonState: VaultItemState.ViewState.Content.Common,
|
||||
@@ -47,52 +51,73 @@ fun VaultItemLoginContent(
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.item_information),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "LoginItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "LoginItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
if (loginItemState.hasLoginCredentials) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.login_credentials),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
}
|
||||
|
||||
loginItemState.username?.let { username ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
UsernameField(
|
||||
username = username,
|
||||
onCopyUsernameClick = vaultLoginItemTypeHandlers.onCopyUsernameClick,
|
||||
cardStyle = loginItemState
|
||||
.passwordData?.let { CardStyle.Top(dividerPadding = 0.dp) }
|
||||
?: CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
loginItemState.passwordData?.let { passwordData ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
PasswordField(
|
||||
passwordData = passwordData,
|
||||
onShowPasswordClick = vaultLoginItemTypeHandlers.onShowPasswordClick,
|
||||
onCheckForBreachClick = vaultLoginItemTypeHandlers.onCheckForBreachClick,
|
||||
onCopyPasswordClick = vaultLoginItemTypeHandlers.onCopyPasswordClick,
|
||||
cardStyle = loginItemState
|
||||
.username?.let { CardStyle.Bottom }
|
||||
?: CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -104,7 +129,7 @@ fun VaultItemLoginContent(
|
||||
creationDate = creationDate.invoke(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -118,41 +143,45 @@ fun VaultItemLoginContent(
|
||||
onCopyTotpClick = vaultLoginItemTypeHandlers.onCopyTotpCodeClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
loginItemState.uris.takeUnless { it.isEmpty() }?.let { uris ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.ur_is),
|
||||
label = stringResource(id = R.string.autofill_options),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(uris) { uriData ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
itemsIndexed(uris) { index, uriData ->
|
||||
UriField(
|
||||
uriData = uriData,
|
||||
onCopyUriClick = vaultLoginItemTypeHandlers.onCopyUriClick,
|
||||
onLaunchUriClick = vaultLoginItemTypeHandlers.onLaunchUriClick,
|
||||
cardStyle = uris.toListItemCardStyle(index = index, dividerPadding = 0.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.notes?.let { notes ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -168,59 +197,64 @@ fun VaultItemLoginContent(
|
||||
|
||||
commonState.customFields.takeUnless { it.isEmpty() }?.let { customFields ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
items(customFields) { customField ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
CustomField(
|
||||
customField = customField,
|
||||
onCopyCustomHiddenField = vaultCommonItemTypeHandlers.onCopyCustomHiddenField,
|
||||
onCopyCustomTextField = vaultCommonItemTypeHandlers.onCopyCustomTextField,
|
||||
onShowHiddenFieldClick = vaultCommonItemTypeHandlers.onShowHiddenFieldClick,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.attachments.takeUnless { it?.isEmpty() == true }?.let { attachments ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.attachments),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(attachments) { attachmentItem ->
|
||||
itemsIndexed(attachments) { index, attachmentItem ->
|
||||
AttachmentItemContent(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
attachmentItem = attachmentItem,
|
||||
onAttachmentDownloadClick =
|
||||
vaultCommonItemTypeHandlers.onAttachmentDownloadClick,
|
||||
cardStyle = attachments.toListItemCardStyle(index = index),
|
||||
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
|
||||
.onAttachmentDownloadClick,
|
||||
)
|
||||
}
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
VaultItemUpdateText(
|
||||
header = "${stringResource(id = R.string.date_updated)}: ",
|
||||
text = commonState.lastUpdated,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -231,7 +265,8 @@ fun VaultItemLoginContent(
|
||||
text = revisionDate,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -243,7 +278,8 @@ fun VaultItemLoginContent(
|
||||
onPasswordHistoryClick = vaultLoginItemTypeHandlers.onPasswordHistoryClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -266,6 +302,7 @@ private fun Fido2CredentialField(
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -283,7 +320,7 @@ private fun NotesField(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = onCopyAction,
|
||||
@@ -291,6 +328,7 @@ private fun NotesField(
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -301,6 +339,7 @@ private fun PasswordField(
|
||||
onShowPasswordClick: (Boolean) -> Unit,
|
||||
onCheckForBreachClick: () -> Unit,
|
||||
onCopyPasswordClick: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (passwordData.canViewPassword) {
|
||||
@@ -313,7 +352,7 @@ private fun PasswordField(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_check_mark,
|
||||
contentDescription = stringResource(
|
||||
id = R.string.check_known_data_breaches_for_this_password,
|
||||
@@ -321,21 +360,23 @@ private fun PasswordField(
|
||||
onClick = onCheckForBreachClick,
|
||||
modifier = Modifier.testTag(tag = "LoginCheckPasswordButton"),
|
||||
)
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_password),
|
||||
onClick = onCopyPasswordClick,
|
||||
modifier = Modifier.testTag(tag = "LoginCopyPasswordButton"),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
showPasswordTestTag = "LoginViewPasswordButton",
|
||||
passwordFieldTestTag = "LoginPasswordEntry",
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
BitwardenHiddenPasswordField(
|
||||
label = stringResource(id = R.string.password),
|
||||
value = passwordData.password,
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier
|
||||
.testTag("LoginPasswordEntry"),
|
||||
)
|
||||
@@ -373,39 +414,41 @@ private fun TotpField(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (enabled) {
|
||||
Row {
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.verification_code_totp),
|
||||
value = totpCodeItemData.verificationCode
|
||||
.chunked(AUTH_CODE_SPACING_INTERVAL)
|
||||
.joinToString(" "),
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
actions = {
|
||||
BitwardenCircularCountdownIndicator(
|
||||
timeLeftSeconds = totpCodeItemData.timeLeftSeconds,
|
||||
periodSeconds = totpCodeItemData.periodSeconds,
|
||||
)
|
||||
BitwardenTonalIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_totp),
|
||||
onClick = onCopyTotpClick,
|
||||
modifier = Modifier.testTag(tag = "LoginCopyTotpButton"),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
textFieldTestTag = "LoginTotpEntry",
|
||||
)
|
||||
}
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.verification_code_totp),
|
||||
value = totpCodeItemData.verificationCode
|
||||
.chunked(AUTH_CODE_SPACING_INTERVAL)
|
||||
.joinToString(" "),
|
||||
onValueChange = { },
|
||||
textStyle = BitwardenTheme.typography.sensitiveInfoSmall,
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
actions = {
|
||||
BitwardenCircularCountdownIndicator(
|
||||
timeLeftSeconds = totpCodeItemData.timeLeftSeconds,
|
||||
periodSeconds = totpCodeItemData.periodSeconds,
|
||||
)
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_totp),
|
||||
onClick = onCopyTotpClick,
|
||||
modifier = Modifier.testTag(tag = "LoginCopyTotpButton"),
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "LoginTotpEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.verification_code_totp),
|
||||
value = stringResource(id = R.string.premium_subscription_required),
|
||||
value = "",
|
||||
supportingText = stringResource(id = R.string.premium_subscription_required),
|
||||
enabled = false,
|
||||
singleLine = false,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -416,6 +459,7 @@ private fun UriField(
|
||||
uriData: VaultItemState.ViewState.Content.ItemType.Login.UriData,
|
||||
onCopyUriClick: (String) -> Unit,
|
||||
onLaunchUriClick: (String) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTextFieldWithActions(
|
||||
@@ -426,7 +470,7 @@ private fun UriField(
|
||||
singleLine = false,
|
||||
actions = {
|
||||
if (uriData.isLaunchable) {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_external_link,
|
||||
contentDescription = stringResource(id = R.string.launch),
|
||||
onClick = { onLaunchUriClick(uriData.uri) },
|
||||
@@ -434,7 +478,7 @@ private fun UriField(
|
||||
)
|
||||
}
|
||||
if (uriData.isCopyable) {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy),
|
||||
onClick = { onCopyUriClick(uriData.uri) },
|
||||
@@ -442,8 +486,9 @@ private fun UriField(
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
textFieldTestTag = "LoginUriEntry",
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -451,6 +496,7 @@ private fun UriField(
|
||||
private fun UsernameField(
|
||||
username: String,
|
||||
onCopyUsernameClick: () -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTextFieldWithActions(
|
||||
@@ -460,14 +506,15 @@ private fun UsernameField(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_username),
|
||||
onClick = onCopyUsernameClick,
|
||||
modifier = Modifier.testTag(tag = "LoginCopyUsernameButton"),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
textFieldTestTag = "LoginUsernameEntry",
|
||||
cardStyle = cardStyle,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
+32
-18
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -16,10 +17,13 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
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.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
|
||||
@@ -35,36 +39,40 @@ fun VaultItemSecureNoteContent(
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.item_information),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "ItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
commonState.notes?.let { notes ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -75,7 +83,7 @@ fun VaultItemSecureNoteContent(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
@@ -83,68 +91,74 @@ fun VaultItemSecureNoteContent(
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.customFields.takeUnless { it.isEmpty() }?.let { customFields ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
items(customFields) { customField ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
CustomField(
|
||||
customField = customField,
|
||||
onCopyCustomHiddenField = vaultCommonItemTypeHandlers.onCopyCustomHiddenField,
|
||||
onCopyCustomTextField = vaultCommonItemTypeHandlers.onCopyCustomTextField,
|
||||
onShowHiddenFieldClick = vaultCommonItemTypeHandlers.onShowHiddenFieldClick,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.attachments.takeUnless { it?.isEmpty() == true }?.let { attachments ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.attachments),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(attachments) { attachmentItem ->
|
||||
itemsIndexed(attachments) { index, attachmentItem ->
|
||||
AttachmentItemContent(
|
||||
modifier = Modifier
|
||||
.testTag("CipherAttachment")
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
attachmentItem = attachmentItem,
|
||||
onAttachmentDownloadClick =
|
||||
vaultCommonItemTypeHandlers.onAttachmentDownloadClick,
|
||||
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
|
||||
.onAttachmentDownloadClick,
|
||||
cardStyle = attachments.toListItemCardStyle(index = index),
|
||||
)
|
||||
}
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp)
|
||||
.semantics(mergeDescendants = true) { },
|
||||
) {
|
||||
Text(
|
||||
|
||||
+37
-23
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
@@ -14,11 +15,13 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
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.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultSshKeyItemTypeHandlers
|
||||
|
||||
@@ -36,26 +39,29 @@ fun VaultItemSshKeyContent(
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.item_information),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
label = stringResource(id = R.string.name),
|
||||
value = commonState.name,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
textFieldTestTag = "SshKeyItemNameEntry",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
textFieldTestTag = "SshKeyItemNameEntry",
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,13 +74,14 @@ fun VaultItemSshKeyContent(
|
||||
singleLine = false,
|
||||
readOnly = true,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_public_key),
|
||||
onClick = vaultSshKeyItemTypeHandlers.onCopyPublicKeyClick,
|
||||
modifier = Modifier.testTag(tag = "SshKeyCopyPublicKeyButton"),
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Top(),
|
||||
modifier = Modifier
|
||||
.testTag("SshKeyItemPublicKeyEntry")
|
||||
.fillMaxWidth()
|
||||
@@ -83,7 +90,6 @@ fun VaultItemSshKeyContent(
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenPasswordFieldWithActions(
|
||||
label = stringResource(id = R.string.private_key),
|
||||
value = sshKeyItemState.privateKey,
|
||||
@@ -91,7 +97,7 @@ fun VaultItemSshKeyContent(
|
||||
singleLine = false,
|
||||
readOnly = true,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_private_key),
|
||||
onClick = vaultSshKeyItemTypeHandlers.onCopyPrivateKeyClick,
|
||||
@@ -101,15 +107,15 @@ fun VaultItemSshKeyContent(
|
||||
showPassword = sshKeyItemState.showPrivateKey,
|
||||
showPasswordTestTag = "ViewPrivateKeyButton",
|
||||
showPasswordChange = vaultSshKeyItemTypeHandlers.onShowPrivateKeyClick,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.testTag("SshKeyItemPrivateKeyEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.fingerprint),
|
||||
value = sshKeyItemState.fingerprint,
|
||||
@@ -117,13 +123,14 @@ fun VaultItemSshKeyContent(
|
||||
singleLine = false,
|
||||
readOnly = true,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_fingerprint),
|
||||
onClick = vaultSshKeyItemTypeHandlers.onCopyFingerprintClick,
|
||||
modifier = Modifier.testTag(tag = "SshKeyCopyFingerprintButton"),
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.testTag("SshKeyItemFingerprintEntry")
|
||||
.fillMaxWidth()
|
||||
@@ -133,12 +140,13 @@ fun VaultItemSshKeyContent(
|
||||
|
||||
commonState.notes?.let { notes ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.notes),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextFieldWithActions(
|
||||
@@ -148,7 +156,7 @@ fun VaultItemSshKeyContent(
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
@@ -156,68 +164,74 @@ fun VaultItemSshKeyContent(
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.customFields.takeUnless { it.isEmpty() }?.let { customFields ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.custom_fields),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
items(customFields) { customField ->
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
CustomField(
|
||||
customField = customField,
|
||||
onCopyCustomHiddenField = vaultCommonItemTypeHandlers.onCopyCustomHiddenField,
|
||||
onCopyCustomTextField = vaultCommonItemTypeHandlers.onCopyCustomTextField,
|
||||
onShowHiddenFieldClick = vaultCommonItemTypeHandlers.onShowHiddenFieldClick,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.attachments.takeUnless { it?.isEmpty() == true }?.let { attachments ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.attachments),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(attachments) { attachmentItem ->
|
||||
itemsIndexed(attachments) { index, attachmentItem ->
|
||||
AttachmentItemContent(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
attachmentItem = attachmentItem,
|
||||
onAttachmentDownloadClick = vaultCommonItemTypeHandlers
|
||||
.onAttachmentDownloadClick,
|
||||
cardStyle = attachments.toListItemCardStyle(index = index),
|
||||
)
|
||||
}
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
VaultItemUpdateText(
|
||||
header = "${stringResource(id = R.string.date_updated)}: ",
|
||||
text = commonState.lastUpdated,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 12.dp)
|
||||
.testTag("SshKeyItemLastUpdated"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||
import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.model.TotpCodeItemData
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.model.VaultItemStateData
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.util.toViewState
|
||||
@@ -1516,6 +1517,15 @@ data class VaultItemState(
|
||||
val fido2CredentialCreationDateText: Text?,
|
||||
) : ItemType() {
|
||||
|
||||
/**
|
||||
* Indicates that at least one of the login credentials are present.
|
||||
*/
|
||||
val hasLoginCredentials: Boolean
|
||||
get() = username != null ||
|
||||
passwordData != null ||
|
||||
fido2CredentialCreationDateText != null ||
|
||||
totpCodeItemData != null
|
||||
|
||||
/**
|
||||
* A wrapper for the password data.
|
||||
*
|
||||
@@ -1571,7 +1581,24 @@ data class VaultItemState(
|
||||
val email: String?,
|
||||
val phone: String?,
|
||||
val address: String?,
|
||||
) : ItemType()
|
||||
) : ItemType() {
|
||||
|
||||
/**
|
||||
* An ordered list of Card specific elements.
|
||||
*/
|
||||
val propertyList: List<String>
|
||||
get() = persistentListOfNotNull(
|
||||
identityName,
|
||||
username,
|
||||
company,
|
||||
ssn,
|
||||
passportNumber,
|
||||
licenseNumber,
|
||||
email,
|
||||
phone,
|
||||
address,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the `Card` item type.
|
||||
@@ -1590,6 +1617,18 @@ data class VaultItemState(
|
||||
val securityCode: CodeData?,
|
||||
) : ItemType() {
|
||||
|
||||
/**
|
||||
* An ordered list of Card specific elements.
|
||||
*/
|
||||
val propertyList: List<Any>
|
||||
get() = persistentListOfNotNull(
|
||||
cardholderName,
|
||||
number,
|
||||
brand.takeIf { brand != VaultCardBrand.SELECT },
|
||||
expiration,
|
||||
securityCode,
|
||||
)
|
||||
|
||||
/**
|
||||
* A wrapper for the number data.
|
||||
*
|
||||
|
||||
+33
-37
@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -17,10 +17,11 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenMasterPasswordDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenGroupItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenListItem
|
||||
@@ -101,25 +102,25 @@ fun VaultItemListingContent(
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
if (showAddTotpBanner) {
|
||||
if (showAddTotpBanner) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = R.string.add_this_authenticator_key_to_a_login),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
if (policyDisablesSend) {
|
||||
if (policyDisablesSend) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenInfoCalloutCard(
|
||||
text = stringResource(id = R.string.send_disabled_warning),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
@@ -127,83 +128,80 @@ fun VaultItemListingContent(
|
||||
|
||||
if (state.displayCollectionList.isNotEmpty()) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.collections),
|
||||
supportingLabel = state.displayCollectionList.count().toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
items(state.displayCollectionList) { collection ->
|
||||
itemsIndexed(state.displayCollectionList) { index, collection ->
|
||||
BitwardenGroupItem(
|
||||
startIcon = rememberVectorPainter(id = R.drawable.ic_collections),
|
||||
label = collection.name,
|
||||
supportingLabel = collection.count.toString(),
|
||||
onClick = { collectionClick(collection.id) },
|
||||
showDivider = false,
|
||||
cardStyle = state
|
||||
.displayCollectionList
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.displayFolderList.isNotEmpty()) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.folders),
|
||||
supportingLabel = state.displayFolderList.count().toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
items(state.displayFolderList) { folder ->
|
||||
itemsIndexed(state.displayFolderList) { index, folder ->
|
||||
BitwardenGroupItem(
|
||||
startIcon = rememberVectorPainter(id = R.drawable.ic_folder),
|
||||
label = folder.name,
|
||||
supportingLabel = folder.count.toString(),
|
||||
onClick = { folderClick(folder.id) },
|
||||
showDivider = false,
|
||||
cardStyle = state
|
||||
.displayFolderList
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.shouldShowDivider) {
|
||||
item {
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.displayItemList.isNotEmpty()) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.items),
|
||||
supportingLabel = state.displayItemList.size.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(state.displayItemList) {
|
||||
itemsIndexed(state.displayItemList) { index, it ->
|
||||
BitwardenListItem(
|
||||
startIcon = it.iconData,
|
||||
startIconTestTag = it.iconTestTag,
|
||||
@@ -263,14 +261,12 @@ fun VaultItemListingContent(
|
||||
// Only show options if allowed
|
||||
.filter { !policyDisablesSend }
|
||||
.toPersistentList(),
|
||||
cardStyle = state
|
||||
.displayItemList
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
// There is some built-in padding to the menu button that makes up
|
||||
// the visual difference here.
|
||||
end = 12.dp,
|
||||
),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -14,7 +14,9 @@ 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.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.components.dropdown.BitwardenMultiSelectButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.components.collectionItemsSelector
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCollection
|
||||
@@ -37,7 +39,7 @@ fun VaultMoveToOrganizationContent(
|
||||
) {
|
||||
if (!showOnlyCollections) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
BitwardenMultiSelectButton(
|
||||
label = stringResource(id = R.string.organization),
|
||||
options = state
|
||||
@@ -52,9 +54,11 @@ fun VaultMoveToOrganizationContent(
|
||||
.first { it.name == selectedString },
|
||||
)
|
||||
},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.testTag("OrganizationListDropdown")
|
||||
.padding(horizontal = 16.dp),
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,7 +71,8 @@ fun VaultMoveToOrganizationContent(
|
||||
color = BitwardenTheme.colorScheme.text.secondary,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 32.dp),
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,21 @@ package com.x8bit.bitwarden.ui.vault.feature.vault
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
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.divider.BitwardenHorizontalDivider
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenGroupItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.toIconResources
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||
@@ -40,6 +43,9 @@ fun VaultContent(
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 12.dp))
|
||||
}
|
||||
if (state.totpItemsCount > 0) {
|
||||
item {
|
||||
BitwardenListHeaderText(
|
||||
@@ -47,8 +53,10 @@ fun VaultContent(
|
||||
supportingLabel = TOTP_TYPES_COUNT.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -57,31 +65,31 @@ fun VaultContent(
|
||||
label = stringResource(id = R.string.verification_codes),
|
||||
supportingLabel = state.totpItemsCount.toString(),
|
||||
onClick = vaultHandlers.verificationCodesClick,
|
||||
showDivider = true,
|
||||
showDivider = false,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("VerificationCodesFilter")
|
||||
.padding(16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.favoriteItems.isNotEmpty()) {
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.favorites),
|
||||
supportingLabel = state.favoriteItems.count().toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
items(state.favoriteItems) { favoriteItem ->
|
||||
itemsIndexed(state.favoriteItems) { index, favoriteItem ->
|
||||
VaultEntryListItem(
|
||||
startIcon = favoriteItem.startIcon,
|
||||
startIconTestTag = favoriteItem.startIconTestTag,
|
||||
@@ -102,39 +110,28 @@ fun VaultContent(
|
||||
vaultHandlers.overflowOptionClick(action)
|
||||
}
|
||||
},
|
||||
cardStyle = state
|
||||
.favoriteItems
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("CipherCell")
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
// There is some built-in padding to the menu button that makes up
|
||||
// the visual difference here.
|
||||
end = 12.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 16.dp, bottom = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.types),
|
||||
supportingLabel = state.itemTypesCount.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -145,10 +142,11 @@ fun VaultContent(
|
||||
supportingLabel = state.loginItemsCount.toString(),
|
||||
onClick = vaultHandlers.loginGroupClick,
|
||||
showDivider = false,
|
||||
cardStyle = CardStyle.Top(dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("LoginFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -160,10 +158,11 @@ fun VaultContent(
|
||||
supportingLabel = state.cardItemsCount.toString(),
|
||||
onClick = vaultHandlers.cardGroupClick,
|
||||
showDivider = false,
|
||||
cardStyle = CardStyle.Middle(dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("CardFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -175,10 +174,11 @@ fun VaultContent(
|
||||
supportingLabel = state.identityItemsCount.toString(),
|
||||
onClick = vaultHandlers.identityGroupClick,
|
||||
showDivider = false,
|
||||
cardStyle = CardStyle.Middle(dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("IdentityFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -190,10 +190,15 @@ fun VaultContent(
|
||||
supportingLabel = state.secureNoteItemsCount.toString(),
|
||||
onClick = vaultHandlers.secureNoteGroupClick,
|
||||
showDivider = false,
|
||||
cardStyle = if (showSshKeys) {
|
||||
CardStyle.Middle(dividerPadding = 56.dp)
|
||||
} else {
|
||||
CardStyle.Bottom
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("SecureNoteFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -206,71 +211,61 @@ fun VaultContent(
|
||||
supportingLabel = state.sshKeyItemsCount.toString(),
|
||||
onClick = vaultHandlers.sshKeyGroupClick,
|
||||
showDivider = false,
|
||||
cardStyle = CardStyle.Bottom,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("SshKeyFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.folderItems.isNotEmpty()) {
|
||||
item {
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 16.dp, bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.folders),
|
||||
supportingLabel = state.folderItems.count().toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
items(state.folderItems) { folder ->
|
||||
itemsIndexed(state.folderItems) { index, folder ->
|
||||
BitwardenGroupItem(
|
||||
startIcon = rememberVectorPainter(id = R.drawable.ic_folder),
|
||||
label = folder.name(),
|
||||
supportingLabel = folder.itemCount.toString(),
|
||||
onClick = { vaultHandlers.folderClick(folder) },
|
||||
showDivider = false,
|
||||
cardStyle = state
|
||||
.folderItems
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("FolderFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.noFolderItems.isNotEmpty()) {
|
||||
item {
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 16.dp, bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.folder_none),
|
||||
supportingLabel = state.noFolderItems.count().toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
items(state.noFolderItems) { noFolderItem ->
|
||||
itemsIndexed(state.noFolderItems) { index, noFolderItem ->
|
||||
VaultEntryListItem(
|
||||
startIcon = noFolderItem.startIcon,
|
||||
startIconTestTag = noFolderItem.startIconTestTag,
|
||||
@@ -291,68 +286,60 @@ fun VaultContent(
|
||||
vaultHandlers.overflowOptionClick(action)
|
||||
}
|
||||
},
|
||||
cardStyle = state
|
||||
.noFolderItems
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("CipherCell")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.collectionItems.isNotEmpty()) {
|
||||
item {
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 16.dp, bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.collections),
|
||||
supportingLabel = state.collectionItems.count().toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
items(state.collectionItems) { collection ->
|
||||
itemsIndexed(state.collectionItems) { index, collection ->
|
||||
BitwardenGroupItem(
|
||||
startIcon = rememberVectorPainter(id = R.drawable.ic_collections),
|
||||
label = collection.name,
|
||||
supportingLabel = collection.itemCount.toString(),
|
||||
onClick = { vaultHandlers.collectionClick(collection) },
|
||||
showDivider = false,
|
||||
cardStyle = state
|
||||
.collectionItems
|
||||
.toListItemCardStyle(index = index, dividerPadding = 56.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("CollectionFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
BitwardenHorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 16.dp, bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 16.dp))
|
||||
BitwardenListHeaderText(
|
||||
label = stringResource(id = R.string.trash),
|
||||
supportingLabel = TRASH_TYPES_COUNT.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.standardHorizontalMargin()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Spacer(modifier = Modifier.height(height = 8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
@@ -362,13 +349,17 @@ fun VaultContent(
|
||||
supportingLabel = state.trashItemsCount.toString(),
|
||||
onClick = vaultHandlers.trashClick,
|
||||
showDivider = false,
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag("TrashFilter")
|
||||
.padding(horizontal = 16.dp),
|
||||
.standardHorizontalMargin(),
|
||||
)
|
||||
}
|
||||
|
||||
item { Spacer(modifier = Modifier.height(88.dp)) }
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(height = 88.dp))
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.BitwardenListItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.listitem.SelectionItemData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
@@ -24,6 +25,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
* @param onClick The lambda to be invoked when the item is clicked.
|
||||
* @param overflowOptions List of options to display for the item.
|
||||
* @param onOverflowOptionClick The lambda to be invoked when an overflow option is clicked.
|
||||
* @param cardStyle Indicates the type of card style to be applied.
|
||||
* @param modifier An optional [Modifier] for this Composable, defaulting to an empty Modifier.
|
||||
* This allows the caller to specify things like padding, size, etc.
|
||||
*/
|
||||
@@ -35,6 +37,7 @@ fun VaultEntryListItem(
|
||||
onClick: () -> Unit,
|
||||
overflowOptions: ImmutableList<ListingItemOverflowAction.VaultAction>,
|
||||
onOverflowOptionClick: (ListingItemOverflowAction.VaultAction) -> Unit,
|
||||
cardStyle: CardStyle,
|
||||
modifier: Modifier = Modifier,
|
||||
trailingLabelIcons: ImmutableList<IconResource> = persistentListOf(),
|
||||
supportingLabel: String? = null,
|
||||
@@ -55,10 +58,11 @@ fun VaultEntryListItem(
|
||||
)
|
||||
}
|
||||
.toImmutableList(),
|
||||
cardStyle = cardStyle,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun VaultEntryListItem_preview() {
|
||||
BitwardenTheme {
|
||||
@@ -70,6 +74,7 @@ private fun VaultEntryListItem_preview() {
|
||||
onClick = {},
|
||||
overflowOptions = persistentListOf(),
|
||||
onOverflowOptionClick = {},
|
||||
cardStyle = CardStyle.Full,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
|
||||
Loaded 100 of 110 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user