From 5b5176db402a0940a0e67d42c38c32929b4c8b4a Mon Sep 17 00:00:00 2001 From: David Perez Date: Tue, 14 Oct 2025 14:59:08 -0500 Subject: [PATCH] PM-26910: Minor UI updates for the Authenticator (#6028) --- .../feature/itemlisting/ItemListingScreen.kt | 143 ++++-------------- .../components/card/BitwardenActionCard.kt | 24 ++- ...BitwardenExpandableFloatingActionButton.kt | 4 +- 3 files changed, 56 insertions(+), 115 deletions(-) diff --git a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt index 2193803c7a..33184d6a03 100644 --- a/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt +++ b/authenticator/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt @@ -2,28 +2,21 @@ package com.bitwarden.authenticator.ui.authenticator.feature.itemlisting import android.Manifest import android.widget.Toast -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image 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.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.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults @@ -64,9 +57,8 @@ import com.bitwarden.ui.platform.base.util.toListItemCardStyle import com.bitwarden.ui.platform.components.appbar.BitwardenMediumTopAppBar import com.bitwarden.ui.platform.components.appbar.action.BitwardenSearchActionItem import com.bitwarden.ui.platform.components.button.BitwardenFilledButton -import com.bitwarden.ui.platform.components.button.BitwardenTextButton +import com.bitwarden.ui.platform.components.button.model.BitwardenButtonData import com.bitwarden.ui.platform.components.card.BitwardenActionCard -import com.bitwarden.ui.platform.components.card.color.bitwardenCardColors import com.bitwarden.ui.platform.components.content.BitwardenLoadingContent import com.bitwarden.ui.platform.components.dialog.BitwardenBasicDialog import com.bitwarden.ui.platform.components.dialog.BitwardenLoadingDialog @@ -213,7 +205,7 @@ fun ItemListingScreen( ), expandableFabIcon = ExpandableFabIcon( icon = IconData.Local( - iconRes = BitwardenDrawable.ic_plus, + iconRes = BitwardenDrawable.ic_plus_large, contentDescription = BitwardenString.add_item.asText(), testTag = "AddItemButton", ), @@ -619,102 +611,6 @@ fun EmptyItemListingContent( } } -@Composable -private fun DownloadBitwardenActionCard( - onDismissClick: () -> Unit, - onDownloadBitwardenClick: () -> Unit, - modifier: Modifier = Modifier, -) = BitwardenActionCard( - modifier = modifier, - cardSubtitle = stringResource(BitwardenString.download_bitwarden_card_message), - actionText = stringResource(BitwardenString.download_now), - cardTitle = stringResource(BitwardenString.download_bitwarden_card_title), - onActionClick = onDownloadBitwardenClick, - leadingContent = { - Icon( - painter = rememberVectorPainter(BitwardenDrawable.ic_shield), - contentDescription = null, - tint = BitwardenTheme.colorScheme.icon.secondary, - ) - }, - onDismissClick = onDismissClick, -) - -@Suppress("LongMethod") -@Composable -private fun SyncWithBitwardenActionCard( - onDismissClick: () -> Unit, - onAppSettingsClick: () -> Unit, - onLearnMoreClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Card( - modifier = modifier, - shape = BitwardenTheme.shapes.actionCard, - colors = bitwardenCardColors(), - elevation = CardDefaults.elevatedCardElevation(), - border = BorderStroke(width = 1.dp, color = BitwardenTheme.colorScheme.stroke.border), - ) { - Spacer(Modifier.height(height = 4.dp)) - Row(modifier = Modifier.fillMaxWidth()) { - Spacer(Modifier.width(width = 16.dp)) - Row( - modifier = Modifier.padding(top = 12.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - painter = rememberVectorPainter(id = BitwardenDrawable.ic_shield), - contentDescription = null, - tint = BitwardenTheme.colorScheme.icon.secondary, - modifier = Modifier.size(size = 20.dp), - ) - Spacer(Modifier.width(width = 16.dp)) - Text( - text = stringResource(id = BitwardenString.sync_with_the_bitwarden_app), - style = BitwardenTheme.typography.bodyLarge, - color = BitwardenTheme.colorScheme.text.primary, - ) - } - Spacer(Modifier.weight(weight = 1f)) - Spacer(Modifier.width(width = 16.dp)) - IconButton(onClick = onDismissClick) { - Icon( - painter = painterResource(id = BitwardenDrawable.ic_close), - contentDescription = stringResource(id = BitwardenString.close), - tint = BitwardenTheme.colorScheme.icon.primary, - modifier = Modifier.size(size = 24.dp), - ) - } - Spacer(Modifier.width(width = 4.dp)) - } - Text( - text = stringResource(id = BitwardenString.sync_with_bitwarden_action_card_message), - style = BitwardenTheme.typography.bodyMedium, - color = BitwardenTheme.colorScheme.text.primary, - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(start = 36.dp, end = 48.dp) - .fillMaxWidth(), - ) - Spacer(Modifier.height(height = 16.dp)) - BitwardenFilledButton( - label = stringResource(id = BitwardenString.take_me_to_app_settings), - onClick = onAppSettingsClick, - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxWidth(), - ) - BitwardenTextButton( - label = stringResource(id = BitwardenString.learn_more), - onClick = onLearnMoreClick, - modifier = Modifier - .padding(horizontal = 16.dp) - .fillMaxWidth(), - ) - Spacer(Modifier.height(height = 4.dp)) - } -} - @Composable private fun ActionCard( actionCardState: ItemListingState.ActionCardState, @@ -727,19 +623,44 @@ private fun ActionCard( ) { when (actionCardState) { ItemListingState.ActionCardState.DownloadBitwardenApp -> { - DownloadBitwardenActionCard( + BitwardenActionCard( modifier = modifier, - onDownloadBitwardenClick = onDownloadBitwardenClick, + cardSubtitle = stringResource(id = BitwardenString.download_bitwarden_card_message), + actionText = stringResource(id = BitwardenString.download_now), + cardTitle = stringResource(id = BitwardenString.download_bitwarden_card_title), + onActionClick = onDownloadBitwardenClick, onDismissClick = onDownloadBitwardenDismissClick, + leadingContent = { + Icon( + painter = rememberVectorPainter(id = BitwardenDrawable.ic_shield), + contentDescription = null, + tint = BitwardenTheme.colorScheme.icon.secondary, + ) + }, ) } ItemListingState.ActionCardState.SyncWithBitwarden -> { - SyncWithBitwardenActionCard( + BitwardenActionCard( modifier = modifier, - onAppSettingsClick = onSyncWithBitwardenClick, + cardTitle = stringResource(id = BitwardenString.sync_with_the_bitwarden_app), + actionText = stringResource(id = BitwardenString.take_me_to_app_settings), + onActionClick = onSyncWithBitwardenClick, + cardSubtitle = stringResource( + id = BitwardenString.sync_with_bitwarden_action_card_message, + ), onDismissClick = onSyncWithBitwardenDismissClick, - onLearnMoreClick = onSyncLearnMoreClick, + secondaryButton = BitwardenButtonData( + label = BitwardenString.learn_more.asText(), + onClick = onSyncLearnMoreClick, + ), + leadingContent = { + Icon( + painter = rememberVectorPainter(id = BitwardenDrawable.ic_refresh), + contentDescription = null, + tint = BitwardenTheme.colorScheme.icon.secondary, + ) + }, ) } diff --git a/ui/src/main/kotlin/com/bitwarden/ui/platform/components/card/BitwardenActionCard.kt b/ui/src/main/kotlin/com/bitwarden/ui/platform/components/card/BitwardenActionCard.kt index 2517e3c82d..8a8061e603 100644 --- a/ui/src/main/kotlin/com/bitwarden/ui/platform/components/card/BitwardenActionCard.kt +++ b/ui/src/main/kotlin/com/bitwarden/ui/platform/components/card/BitwardenActionCard.kt @@ -26,15 +26,19 @@ import androidx.compose.ui.layout.positionInParent import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.bitwarden.ui.platform.base.util.nullableTestTag import com.bitwarden.ui.platform.base.util.toDp import com.bitwarden.ui.platform.components.badge.NotificationBadge import com.bitwarden.ui.platform.components.button.BitwardenFilledButton import com.bitwarden.ui.platform.components.button.BitwardenStandardIconButton +import com.bitwarden.ui.platform.components.button.BitwardenTextButton +import com.bitwarden.ui.platform.components.button.model.BitwardenButtonData import com.bitwarden.ui.platform.components.card.color.bitwardenCardColors import com.bitwarden.ui.platform.components.util.rememberVectorPainter import com.bitwarden.ui.platform.resource.BitwardenDrawable import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.ui.platform.theme.BitwardenTheme +import com.bitwarden.ui.util.asText /** * A design component action card, which contains a title, action button, and a dismiss button @@ -44,8 +48,9 @@ import com.bitwarden.ui.platform.theme.BitwardenTheme * @param actionText The text content on the CTA button. * @param onActionClick The action to perform when the CTA button is clicked. * @param onDismissClick Optional action to perform when the dismiss button is clicked. - * @param leadingContent Optional content to display on the leading side of the - * [cardTitle] [Text]. + * @param cardSubtitle The subtitle of the card. + * @param secondaryButton The optional data for a secondary button. + * @param leadingContent Optional content to display on the leading side of the [cardTitle] [Text]. */ @Suppress("LongMethod") @Composable @@ -56,6 +61,7 @@ fun BitwardenActionCard( modifier: Modifier = Modifier, onDismissClick: (() -> Unit)? = null, cardSubtitle: String? = null, + secondaryButton: BitwardenButtonData? = null, leadingContent: @Composable (() -> Unit)? = null, ) { Card( @@ -132,6 +138,16 @@ fun BitwardenActionCard( .padding(horizontal = 16.dp) .fillMaxWidth(), ) + secondaryButton?.let { + BitwardenTextButton( + label = it.label(), + onClick = it.onClick, + modifier = Modifier + .padding(horizontal = 16.dp) + .nullableTestTag(tag = it.testTag) + .fillMaxWidth(), + ) + } Spacer(modifier = Modifier.height(height = 16.dp)) } } @@ -199,6 +215,10 @@ private fun BitwardenActionCardWithSubtitle_preview() { actionText = "Action", onActionClick = {}, onDismissClick = {}, + secondaryButton = BitwardenButtonData( + label = "Learn More".asText(), + onClick = {}, + ), leadingContent = { NotificationBadge( notificationCount = 1, diff --git a/ui/src/main/kotlin/com/bitwarden/ui/platform/components/fab/BitwardenExpandableFloatingActionButton.kt b/ui/src/main/kotlin/com/bitwarden/ui/platform/components/fab/BitwardenExpandableFloatingActionButton.kt index d66288046e..bf99cf2eb0 100644 --- a/ui/src/main/kotlin/com/bitwarden/ui/platform/components/fab/BitwardenExpandableFloatingActionButton.kt +++ b/ui/src/main/kotlin/com/bitwarden/ui/platform/components/fab/BitwardenExpandableFloatingActionButton.kt @@ -97,7 +97,7 @@ fun BitwardenExpandableFloatingActionButton( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.spacedBy( - space = 12.dp, + space = 8.dp, alignment = Alignment.Bottom, ), contentPadding = PaddingValues(bottom = 16.dp), @@ -167,7 +167,7 @@ private fun ExpandableFabOption( ) { Text( text = expandableFabOption.label(), - style = BitwardenTheme.typography.labelSmall, + style = BitwardenTheme.typography.labelLarge, modifier = Modifier.padding(all = 8.dp), ) Icon(