Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b94a74c
[PM-25423] Add BidiTextManager for handling bidirectional text
SaintPatrck Sep 3, 2025
6bfd559
[PM-25423] Enhance BidiTextManager with forceLtr, forceRtl, and formaโ€ฆ
SaintPatrck Oct 23, 2025
c2d1eac
[PM-25423] Add Hilt DI module for BidiTextManager
SaintPatrck Oct 23, 2025
12ce07c
[PM-25423] Foundation complete - checkpoint for parallel workstreams
SaintPatrck Oct 23, 2025
5e8a5b9
[PM-25423] Apply BidiTextManager to authenticator VaultVerificationCoโ€ฆ
SaintPatrck Oct 27, 2025
fd742cf
[PM-25423] Apply BidiTextManager to main app VerificationCodeItem forโ€ฆ
SaintPatrck Oct 27, 2025
667d9e2
[PM-25423] Apply BidiTextManager to login item TOTP field for RTL supโ€ฆ
SaintPatrck Oct 27, 2025
184e37d
[PM-25423] Apply BidiTextManager forceLtr to PIN input dialog for RTLโ€ฆ
SaintPatrck Oct 27, 2025
7ec6c1e
[PM-25423] Apply BidiTextManager forceLtr to BitwardenPinDialog for Rโ€ฆ
SaintPatrck Oct 27, 2025
cc21717
[PM-25423] Apply BidiTextManager to card fields for RTL support
SaintPatrck Oct 27, 2025
a099226
[PM-25423] Apply BidiTextManager formatCardNumber to card number inpuโ€ฆ
SaintPatrck Oct 27, 2025
1ebed91
[PM-25423] Apply BidiTextManager unicodeWrap to password field for miโ€ฆ
SaintPatrck Oct 27, 2025
252e9c5
[PM-25423] Apply BidiTextManager unicodeWrap to account email displayโ€ฆ
SaintPatrck Oct 27, 2025
bec2154
[PM-25423] Apply BidiTextManager unicodeWrap to identity email fieldsโ€ฆ
SaintPatrck Oct 27, 2025
5e5e81f
[PM-25423] Apply BidiTextManager unicodeWrap to URI fields for RTL doโ€ฆ
SaintPatrck Oct 27, 2025
26bc6df
[PM-25423] Apply BidiTextManager to card expiration and cardholder naโ€ฆ
SaintPatrck Oct 27, 2025
fc421ac
[PM-25423] Apply BidiTextManager to identity fields (SSN, passport, lโ€ฆ
SaintPatrck Oct 27, 2025
75a4c9d
[PM-25423] Fix merge conflict: Remove duplicate bidiTextManager declaโ€ฆ
SaintPatrck Oct 27, 2025
6b67194
[PM-25423] Fix critical: Provide LocalBidiTextManager in CompositionLโ€ฆ
SaintPatrck Oct 27, 2025
407f20b
[PM-25423] Reorder imports in VaultItemCardContent and VaultItemLoginโ€ฆ
SaintPatrck Oct 27, 2025
12625da
Remove card number formatting from BidiTextManager
SaintPatrck Oct 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ import com.bitwarden.cxf.ui.composition.LocalCredentialExchangeImporter
import com.bitwarden.cxf.ui.composition.LocalCredentialExchangeRequestValidator
import com.bitwarden.cxf.validator.CredentialExchangeRequestValidator
import com.bitwarden.cxf.validator.dsl.credentialExchangeRequestValidator
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.composition.LocalIntentManager
import com.bitwarden.ui.platform.manager.BidiTextManager
import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.platform.manager.dsl.bidiTextManager
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.platform.manager.util.AppResumeStateManager
import com.x8bit.bitwarden.data.platform.manager.util.AppResumeStateManagerImpl
Expand Down Expand Up @@ -79,6 +82,7 @@ fun LocalManagerProvider(
},
credentialExchangeRequestValidator: CredentialExchangeRequestValidator =
credentialExchangeRequestValidator(activity = activity),
bidiTextManager: BidiTextManager = bidiTextManager(),
authTabLaunchers: AuthTabLaunchers,
content: @Composable () -> Unit,
) {
Expand All @@ -97,6 +101,7 @@ fun LocalManagerProvider(
LocalCredentialExchangeImporter provides credentialExchangeImporter,
LocalCredentialExchangeCompletionManager provides credentialExchangeCompletionManager,
LocalCredentialExchangeRequestValidator provides credentialExchangeRequestValidator,
LocalBidiTextManager provides bidiTextManager,
LocalAuthTabLaunchers provides authTabLaunchers,
content = content,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.bitwarden.ui.R
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.base.util.cardStyle
import com.bitwarden.ui.platform.base.util.hexToColor
import com.bitwarden.ui.platform.base.util.toSafeOverlayColor
Expand Down Expand Up @@ -51,6 +52,7 @@ fun AccountSummaryListItem(
clickable: Boolean,
onClick: (userId: String) -> Unit = {},
) {
val bidiTextManager = LocalBidiTextManager.current
Row(
modifier = modifier
.testTag("AccountSummaryListItem")
Expand Down Expand Up @@ -95,7 +97,7 @@ fun AccountSummaryListItem(
modifier = Modifier.weight(1f),
) {
Text(
text = item.email,
text = bidiTextManager.unicodeWrap(item.email),
style = BitwardenTheme.typography.bodyLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.bitwarden.ui.platform.components.field.BitwardenTextField
import com.bitwarden.ui.platform.components.header.BitwardenListHeaderText
import com.bitwarden.ui.platform.components.model.CardStyle
import com.bitwarden.ui.platform.components.text.BitwardenHyperTextLink
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
Expand All @@ -48,6 +49,7 @@ fun VaultItemCardContent(
vaultCardItemTypeHandlers: VaultCardItemTypeHandlers,
modifier: Modifier = Modifier,
) {
val bidiTextManager = LocalBidiTextManager.current
var isExpanded by rememberSaveable { mutableStateOf(value = false) }
val applyIconBackground = cardState.paymentCardBrandIconData == null
LazyColumn(modifier = modifier.fillMaxWidth()) {
Expand All @@ -72,7 +74,7 @@ fun VaultItemCardContent(
item(key = "cardholderName") {
BitwardenTextField(
label = stringResource(id = BitwardenString.cardholder_name),
value = cardholderName,
value = bidiTextManager.unicodeWrap(cardholderName),
onValueChange = {},
readOnly = true,
singleLine = false,
Expand All @@ -94,7 +96,7 @@ fun VaultItemCardContent(
item(key = "cardNumber") {
BitwardenPasswordField(
label = stringResource(id = BitwardenString.number),
value = numberData.number,
value = bidiTextManager.formatCardNumber(numberData.number),
onValueChange = {},
showPassword = numberData.isVisible,
showPasswordChange = vaultCardItemTypeHandlers.onShowNumberClick,
Expand Down Expand Up @@ -174,7 +176,7 @@ fun VaultItemCardContent(
item(key = "securityCode") {
BitwardenPasswordField(
label = stringResource(id = BitwardenString.security_code),
value = securityCodeData.code,
value = bidiTextManager.forceLtr(securityCodeData.code),
onValueChange = {},
showPassword = securityCodeData.isVisible,
showPasswordChange = vaultCardItemTypeHandlers.onShowSecurityCodeClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.base.util.toListItemCardStyle
import com.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
import com.bitwarden.ui.platform.components.field.BitwardenTextField
Expand Down Expand Up @@ -47,6 +48,7 @@ fun VaultItemIdentityContent(
vaultIdentityItemTypeHandlers: VaultIdentityItemTypeHandlers,
modifier: Modifier = Modifier,
) {
val bidiTextManager = LocalBidiTextManager.current
var isExpanded by rememberSaveable { mutableStateOf(value = false) }
LazyColumn(
modifier = modifier.fillMaxWidth(),
Expand Down Expand Up @@ -138,7 +140,7 @@ fun VaultItemIdentityContent(
item(key = "ssn") {
IdentityCopyField(
label = stringResource(id = BitwardenString.ssn),
value = ssn,
value = bidiTextManager.forceLtr(ssn),
copyContentDescription = stringResource(id = BitwardenString.copy_ssn),
textFieldTestTag = "IdentitySsnEntry",
copyActionTestTag = "IdentityCopySsnButton",
Expand All @@ -160,7 +162,7 @@ fun VaultItemIdentityContent(
item(key = "passportNumber") {
IdentityCopyField(
label = stringResource(id = BitwardenString.passport_number),
value = passportNumber,
value = bidiTextManager.unicodeWrap(passportNumber),
copyContentDescription = stringResource(id = BitwardenString.copy_passport_number),
textFieldTestTag = "IdentityPassportNumberEntry",
copyActionTestTag = "IdentityCopyPassportNumberButton",
Expand All @@ -182,7 +184,7 @@ fun VaultItemIdentityContent(
item(key = "licenseNumber") {
IdentityCopyField(
label = stringResource(id = BitwardenString.license_number),
value = licenseNumber,
value = bidiTextManager.unicodeWrap(licenseNumber),
copyContentDescription = stringResource(id = BitwardenString.copy_license_number),
textFieldTestTag = "IdentityLicenseNumberEntry",
copyActionTestTag = "IdentityCopyLicenseNumberButton",
Expand All @@ -204,7 +206,7 @@ fun VaultItemIdentityContent(
item(key = "email") {
IdentityCopyField(
label = stringResource(id = BitwardenString.email),
value = email,
value = bidiTextManager.unicodeWrap(email),
copyContentDescription = stringResource(id = BitwardenString.copy_email),
textFieldTestTag = "IdentityEmailEntry",
copyActionTestTag = "IdentityCopyEmailButton",
Expand All @@ -226,7 +228,7 @@ fun VaultItemIdentityContent(
item(key = "phone") {
IdentityCopyField(
label = stringResource(id = BitwardenString.phone),
value = phone,
value = bidiTextManager.formatPhoneNumber(phone),
copyContentDescription = stringResource(id = BitwardenString.copy_phone),
textFieldTestTag = "IdentityPhoneEntry",
copyActionTestTag = "IdentityCopyPhoneButton",
Expand All @@ -248,7 +250,7 @@ fun VaultItemIdentityContent(
item(key = "address") {
IdentityCopyField(
label = stringResource(id = BitwardenString.address),
value = address,
value = bidiTextManager.unicodeWrap(address),
copyContentDescription = stringResource(id = BitwardenString.copy_address),
textFieldTestTag = "IdentityAddressEntry",
copyActionTestTag = "IdentityCopyAddressButton",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import com.bitwarden.ui.platform.components.model.CardStyle
import com.bitwarden.ui.platform.components.model.TooltipData
import com.bitwarden.ui.platform.components.text.BitwardenClickableText
import com.bitwarden.ui.platform.components.text.BitwardenHyperTextLink
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.manager.BidiTextManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
Expand All @@ -55,6 +57,7 @@ fun VaultItemLoginContent(
vaultLoginItemTypeHandlers: VaultLoginItemTypeHandlers,
modifier: Modifier = Modifier,
) {
val bidiTextManager = LocalBidiTextManager.current
var isExpanded by rememberSaveable { mutableStateOf(value = false) }
LazyColumn(
modifier = modifier.fillMaxWidth(),
Expand Down Expand Up @@ -174,6 +177,7 @@ fun VaultItemLoginContent(
) { index, uriData ->
UriField(
uriData = uriData,
bidiTextManager = bidiTextManager,
onCopyUriClick = vaultLoginItemTypeHandlers.onCopyUriClick,
onLaunchUriClick = vaultLoginItemTypeHandlers.onLaunchUriClick,
cardStyle = uris.toListItemCardStyle(index = index, dividerPadding = 0.dp),
Expand Down Expand Up @@ -391,10 +395,11 @@ private fun PasswordField(
cardStyle: CardStyle,
modifier: Modifier = Modifier,
) {
val bidiTextManager = LocalBidiTextManager.current
if (passwordData.canViewPassword) {
BitwardenPasswordField(
label = stringResource(id = BitwardenString.password),
value = passwordData.password,
value = bidiTextManager.unicodeWrap(passwordData.password),
showPasswordChange = { onShowPasswordClick(it) },
showPassword = passwordData.isVisible,
onValueChange = { },
Expand Down Expand Up @@ -429,7 +434,7 @@ private fun PasswordField(
} else {
BitwardenHiddenPasswordField(
label = stringResource(id = BitwardenString.password),
value = passwordData.password,
value = bidiTextManager.unicodeWrap(passwordData.password),
passwordFieldTestTag = "LoginPasswordEntry",
cardStyle = cardStyle,
modifier = modifier,
Expand All @@ -445,12 +450,15 @@ private fun TotpField(
onAuthenticatorHelpToolTipClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val bidiTextManager = LocalBidiTextManager.current

if (enabled) {
BitwardenTextField(
label = stringResource(id = BitwardenString.authenticator_key),
value = totpCodeItemData.verificationCode
.chunked(AUTH_CODE_SPACING_INTERVAL)
.joinToString(" "),
value = bidiTextManager.formatVerificationCode(
totpCodeItemData.verificationCode,
chunkSize = AUTH_CODE_SPACING_INTERVAL,
),
onValueChange = { },
textStyle = BitwardenTheme.typography.sensitiveInfoSmall,
readOnly = true,
Expand Down Expand Up @@ -497,14 +505,15 @@ private fun TotpField(
@Composable
private fun UriField(
uriData: VaultItemState.ViewState.Content.ItemType.Login.UriData,
bidiTextManager: BidiTextManager,
onCopyUriClick: (String) -> Unit,
onLaunchUriClick: (String) -> Unit,
cardStyle: CardStyle,
modifier: Modifier = Modifier,
) {
BitwardenTextField(
label = stringResource(id = BitwardenString.website_uri),
value = uriData.uri,
value = bidiTextManager.unicodeWrap(uriData.uri),
onValueChange = { },
readOnly = true,
singleLine = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.bitwarden.ui.platform.components.icon.BitwardenIcon
import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.platform.components.indicator.BitwardenCircularCountdownIndicator
import com.bitwarden.ui.platform.components.model.CardStyle
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
Expand Down Expand Up @@ -55,6 +56,8 @@ fun VaultVerificationCodeItem(
modifier: Modifier = Modifier,
supportingLabel: String? = null,
) {
val bidiTextManager = LocalBidiTextManager.current

Row(
modifier = modifier
.defaultMinSize(minHeight = 60.dp)
Expand Down Expand Up @@ -106,7 +109,7 @@ fun VaultVerificationCodeItem(

if (!hideAuthCode) {
Text(
text = authCode.chunked(3).joinToString(" "),
text = bidiTextManager.formatVerificationCode(authCode, chunkSize = 3),
style = BitwardenTheme.typography.sensitiveInfoSmall,
color = BitwardenTheme.colorScheme.text.primary,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.bitwarden.cxf.manager.CredentialExchangeCompletionManager
import com.bitwarden.cxf.validator.CredentialExchangeRequestValidator
import com.bitwarden.ui.platform.base.BaseComposeTest
import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
import com.bitwarden.ui.platform.manager.BidiTextManager
import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.data.platform.manager.util.AppResumeStateManager
Expand Down Expand Up @@ -48,6 +49,7 @@ abstract class BitwardenComposeTest : BaseComposeTest() {
credentialExchangeImporter: CredentialExchangeImporter = mockk(),
credentialExchangeCompletionManager: CredentialExchangeCompletionManager = mockk(),
credentialExchangeRequestValidator: CredentialExchangeRequestValidator = mockk(),
bidiTextManager: BidiTextManager = mockk(),
test: @Composable () -> Unit,
) {
setTestContent {
Expand All @@ -67,6 +69,7 @@ abstract class BitwardenComposeTest : BaseComposeTest() {
credentialExchangeImporter = credentialExchangeImporter,
credentialExchangeCompletionManager = credentialExchangeCompletionManager,
credentialExchangeRequestValidator = credentialExchangeRequestValidator,
bidiTextManager = bidiTextManager,
) {
BitwardenTheme(
theme = theme,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.platform.components.snackbar.model.BitwardenSnackbarData
import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.platform.manager.dsl.bidiTextManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.util.asText
Expand Down Expand Up @@ -71,6 +72,7 @@ class VaultItemScreenTest : BitwardenComposeTest() {
private var onNavigateToPasswordHistoryId: String? = null

private val intentManager = mockk<IntentManager>(relaxed = true)
private val bidiTextManager = bidiTextManager()

private val mutableEventFlow = bufferedMutableSharedFlow<VaultItemEvent>()
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
Expand All @@ -83,6 +85,7 @@ class VaultItemScreenTest : BitwardenComposeTest() {
fun setUp() {
setContent(
intentManager = intentManager,
bidiTextManager = bidiTextManager,
) {
VaultItemScreen(
viewModel = viewModel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.bitwarden.ui.platform.components.icon.BitwardenIcon
import com.bitwarden.ui.platform.components.icon.model.IconData
import com.bitwarden.ui.platform.components.indicator.BitwardenCircularCountdownIndicator
import com.bitwarden.ui.platform.components.model.CardStyle
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.resource.BitwardenDrawable
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.platform.theme.BitwardenTheme
Expand Down Expand Up @@ -98,6 +99,8 @@ fun VaultVerificationCodeItem(
cardStyle: CardStyle,
modifier: Modifier = Modifier,
) {
val bidiTextManager = LocalBidiTextManager.current

Row(
modifier = modifier
.testTag(tag = "Item")
Expand Down Expand Up @@ -154,7 +157,7 @@ fun VaultVerificationCodeItem(

Text(
modifier = Modifier.testTag(tag = "AuthCode"),
text = authCode.chunked(size = 3).joinToString(separator = " "),
text = bidiTextManager.formatVerificationCode(authCode, chunkSize = 3),
style = BitwardenTheme.typography.sensitiveInfoSmall,
color = BitwardenTheme.colorScheme.text.primary,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ import com.bitwarden.authenticator.ui.platform.manager.exit.ExitManagerImpl
import com.bitwarden.authenticator.ui.platform.manager.permissions.PermissionsManager
import com.bitwarden.authenticator.ui.platform.manager.permissions.PermissionsManagerImpl
import com.bitwarden.core.data.manager.BuildInfoManager
import com.bitwarden.ui.platform.composition.LocalBidiTextManager
import com.bitwarden.ui.platform.composition.LocalIntentManager
import com.bitwarden.ui.platform.manager.BidiTextManager
import com.bitwarden.ui.platform.manager.IntentManager
import com.bitwarden.ui.platform.manager.dsl.bidiTextManager
import java.time.Clock

/**
Expand All @@ -34,13 +37,15 @@ fun LocalManagerProvider(
intentManager: IntentManager = IntentManager.create(activity, clock, buildInfoManager),
exitManager: ExitManager = ExitManagerImpl(activity),
biometricsManager: BiometricsManager = BiometricsManagerImpl(activity),
bidiTextManager: BidiTextManager = bidiTextManager(),
content: @Composable () -> Unit,
) {
CompositionLocalProvider(
LocalPermissionsManager provides permissionsManager,
LocalIntentManager provides intentManager,
LocalExitManager provides exitManager,
LocalBiometricsManager provides biometricsManager,
LocalBidiTextManager provides bidiTextManager,
content = content,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@file:OmitFromCoverage

package com.bitwarden.ui.platform.composition

import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.compositionLocalOf
import com.bitwarden.annotation.OmitFromCoverage
import com.bitwarden.ui.platform.manager.BidiTextManager

/**
* CompositionLocal for [BidiTextManager].
*/
val LocalBidiTextManager: ProvidableCompositionLocal<BidiTextManager> = compositionLocalOf {
error("CompositionLocal BidiTextManager not present")
}
Loading
Loading