Skip to content

Commit 2027a66

Browse files
authored
[PM-18714] Display Card brand icon when it is known (#4805)
1 parent ef6d9bc commit 2027a66

30 files changed

+601
-9
lines changed

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemCardContent.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.navigationBarsPadding
77
import androidx.compose.foundation.layout.padding
88
import androidx.compose.foundation.layout.wrapContentWidth
99
import androidx.compose.foundation.lazy.LazyColumn
10-
import androidx.compose.foundation.lazy.items
1110
import androidx.compose.foundation.lazy.itemsIndexed
1211
import androidx.compose.runtime.Composable
1312
import androidx.compose.runtime.getValue
@@ -49,19 +48,21 @@ fun VaultItemCardContent(
4948
modifier: Modifier = Modifier,
5049
) {
5150
var isExpanded by rememberSaveable { mutableStateOf(value = false) }
51+
val applyIconBackground = cardState.paymentCardBrandIconData == null
5252
LazyColumn(modifier = modifier.fillMaxWidth()) {
5353
item {
5454
Spacer(Modifier.height(height = 12.dp))
5555
}
5656
itemHeader(
5757
value = commonState.name,
5858
isFavorite = commonState.favorite,
59-
iconData = commonState.iconData,
59+
iconData = cardState.paymentCardBrandIconData ?: commonState.iconData,
6060
relatedLocations = commonState.relatedLocations,
6161
iconTestTag = "CardItemNameIcon",
6262
textFieldTestTag = "CardItemNameEntry",
6363
isExpanded = isExpanded,
6464
onExpandClick = { isExpanded = !isExpanded },
65+
applyIconBackground = applyIconBackground,
6566
)
6667
item {
6768
Spacer(modifier = Modifier.height(height = 8.dp))

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemIdentityContent.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconBu
2525
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
2626
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
2727
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
28+
import com.x8bit.bitwarden.ui.platform.components.model.IconData
2829
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenHyperTextLink
2930
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
3031
import com.x8bit.bitwarden.ui.vault.feature.item.component.CustomField
@@ -61,6 +62,7 @@ fun VaultItemIdentityContent(
6162
textFieldTestTag = "IdentityItemNameEntry",
6263
isExpanded = isExpanded,
6364
onExpandClick = { isExpanded = !isExpanded },
65+
applyIconBackground = commonState.iconData is IconData.Local,
6466
)
6567
item {
6668
Spacer(modifier = Modifier.height(height = 8.dp))

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
2828
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
2929
import com.x8bit.bitwarden.ui.platform.components.indicator.BitwardenCircularCountdownIndicator
3030
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
31+
import com.x8bit.bitwarden.ui.platform.components.model.IconData
3132
import com.x8bit.bitwarden.ui.platform.components.model.TooltipData
3233
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenClickableText
3334
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenHyperTextLink
@@ -69,6 +70,7 @@ fun VaultItemLoginContent(
6970
textFieldTestTag = "LoginItemNameEntry",
7071
isExpanded = isExpanded,
7172
onExpandClick = { isExpanded = !isExpanded },
73+
applyIconBackground = commonState.iconData is IconData.Local,
7274
)
7375
if (loginItemState.hasLoginCredentials) {
7476
item(key = "loginCredentialsHeader") {

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemSecureNoteContent.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.x8bit.bitwarden.ui.platform.components.button.BitwardenStandardIconBu
2727
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
2828
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
2929
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
30+
import com.x8bit.bitwarden.ui.platform.components.model.IconData
3031
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenHyperTextLink
3132
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
3233
import com.x8bit.bitwarden.ui.vault.feature.item.component.CustomField
@@ -57,6 +58,7 @@ fun VaultItemSecureNoteContent(
5758
textFieldTestTag = "SecureNoteItemNameEntry",
5859
isExpanded = isExpanded,
5960
onExpandClick = { isExpanded = !isExpanded },
61+
applyIconBackground = commonState.iconData is IconData.Local,
6062
)
6163
item {
6264
Spacer(modifier = Modifier.height(height = 8.dp))

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemSshKeyContent.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordField
2525
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
2626
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
2727
import com.x8bit.bitwarden.ui.platform.components.model.CardStyle
28+
import com.x8bit.bitwarden.ui.platform.components.model.IconData
2829
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenHyperTextLink
2930
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
3031
import com.x8bit.bitwarden.ui.vault.feature.item.component.CustomField
@@ -59,6 +60,7 @@ fun VaultItemSshKeyContent(
5960
textFieldTestTag = "SshKeyItemNameEntry",
6061
isExpanded = isExpanded,
6162
onExpandClick = { isExpanded = !isExpanded },
63+
applyIconBackground = commonState.iconData is IconData.Local,
6264
)
6365
item(key = "publicKey") {
6466
Spacer(modifier = Modifier.height(8.dp))

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,13 +1758,15 @@ data class VaultItemState(
17581758
* @property brand The brand for the card.
17591759
* @property expiration The expiration for the card.
17601760
* @property securityCode The securityCode for the card.
1761+
* @property paymentCardBrandIconData The payment card brand icon data for the card.
17611762
*/
17621763
data class Card(
17631764
val cardholderName: String?,
17641765
val number: NumberData?,
17651766
val brand: VaultCardBrand?,
17661767
val expiration: String?,
17671768
val securityCode: CodeData?,
1769+
val paymentCardBrandIconData: IconData?,
17681770
) : ItemType() {
17691771

17701772
/**

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/component/ItemHeader.kt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.compose.runtime.remember
2424
import androidx.compose.runtime.setValue
2525
import androidx.compose.ui.Alignment
2626
import androidx.compose.ui.Modifier
27+
import androidx.compose.ui.graphics.Color
2728
import androidx.compose.ui.graphics.RectangleShape
2829
import androidx.compose.ui.graphics.vector.VectorPainter
2930
import androidx.compose.ui.platform.testTag
@@ -53,6 +54,16 @@ private const val EXPANDABLE_THRESHOLD = 2
5354

5455
/**
5556
* Reusable composable for displaying the cipher name, favorite status, and related locations.
57+
*
58+
* @param value The name of the cipher.
59+
* @param isFavorite Whether the cipher is a favorite.
60+
* @param relatedLocations The locations the cipher is assigned to.
61+
* @param iconData The icon to be displayed.
62+
* @param isExpanded Whether the related locations are expanded.
63+
* @param applyIconBackground Whether a background should be applied to the header icon.
64+
* @param iconTestTag The test tag for the icon.
65+
* @param textFieldTestTag The test tag for the name field.
66+
* @param onExpandClick The action to be performed when the expandable text row is clicked.
5667
*/
5768
@Suppress("LongMethod", "LongParameterList")
5869
fun LazyListScope.itemHeader(
@@ -61,6 +72,7 @@ fun LazyListScope.itemHeader(
6172
relatedLocations: ImmutableList<VaultItemLocation>,
6273
iconData: IconData,
6374
isExpanded: Boolean,
75+
applyIconBackground: Boolean,
6476
iconTestTag: String? = null,
6577
textFieldTestTag: String? = null,
6678
onExpandClick: () -> Unit,
@@ -81,6 +93,7 @@ fun LazyListScope.itemHeader(
8193
ItemHeaderIcon(
8294
iconData = iconData,
8395
testTag = iconTestTag,
96+
applyBackgroundFill = applyIconBackground,
8497
modifier = Modifier.size(36.dp),
8598
)
8699
BitwardenTextField(
@@ -203,14 +216,14 @@ fun LazyListScope.itemHeader(
203216
@Composable
204217
private fun ItemHeaderIcon(
205218
iconData: IconData,
219+
applyBackgroundFill: Boolean,
206220
modifier: Modifier = Modifier,
207221
testTag: String? = null,
208222
) {
209-
val isLocalIcon = iconData is IconData.Local
210223
Box(
211224
contentAlignment = Alignment.Center,
212225
modifier = modifier.then(
213-
if (isLocalIcon) {
226+
if (applyBackgroundFill) {
214227
Modifier.background(
215228
color = BitwardenTheme.colorScheme.illustration.backgroundPrimary,
216229
shape = BitwardenTheme.shapes.favicon,
@@ -223,11 +236,15 @@ private fun ItemHeaderIcon(
223236
BitwardenIcon(
224237
iconData = iconData,
225238
contentDescription = null,
226-
tint = BitwardenTheme.colorScheme.illustration.outline,
239+
tint = if (applyBackgroundFill) {
240+
BitwardenTheme.colorScheme.illustration.outline
241+
} else {
242+
Color.Unspecified
243+
},
227244
modifier = Modifier
228245
.nullableTestTag(testTag)
229246
.then(
230-
if (!isLocalIcon) Modifier.fillMaxSize() else Modifier,
247+
if (!applyBackgroundFill) Modifier.fillMaxSize() else Modifier,
231248
),
232249
)
233250
}
@@ -281,6 +298,7 @@ private fun ItemHeader_LocalIcon_Preview() {
281298
relatedLocations = persistentListOf(),
282299
isExpanded = isExpanded,
283300
onExpandClick = { isExpanded = !isExpanded },
301+
applyIconBackground = true,
284302
)
285303
}
286304
}
@@ -302,6 +320,7 @@ private fun ItemHeader_NetworkIcon_Preview() {
302320
relatedLocations = persistentListOf(),
303321
isExpanded = isExpanded,
304322
onExpandClick = { isExpanded = !isExpanded },
323+
applyIconBackground = false,
305324
)
306325
}
307326
}
@@ -324,6 +343,7 @@ private fun ItemHeader_Organization_Preview() {
324343
),
325344
isExpanded = isExpanded,
326345
onExpandClick = { isExpanded = !isExpanded },
346+
applyIconBackground = true,
327347
)
328348
}
329349
}
@@ -347,6 +367,7 @@ private fun ItemNameField_Org_SingleCollection_Preview() {
347367
),
348368
isExpanded = isExpanded,
349369
onExpandClick = { isExpanded = !isExpanded },
370+
applyIconBackground = true,
350371
)
351372
}
352373
}
@@ -362,7 +383,7 @@ private fun ItemNameField_Org_MultiCollection_Preview() {
362383
value = "Login without favicon",
363384
isFavorite = true,
364385
iconData = IconData.Local(
365-
iconRes = R.drawable.ic_globe,
386+
iconRes = R.drawable.ic_payment_card_brand_visa,
366387
),
367388
relatedLocations = persistentListOf(
368389
VaultItemLocation.Organization("Stark Industries"),
@@ -371,6 +392,7 @@ private fun ItemNameField_Org_MultiCollection_Preview() {
371392
),
372393
isExpanded = isExpanded,
373394
onExpandClick = { isExpanded = !isExpanded },
395+
applyIconBackground = false,
374396
)
375397
}
376398
}
@@ -395,6 +417,7 @@ private fun ItemNameField_Org_SingleCollection_Folder_Preview() {
395417
),
396418
isExpanded = isExpanded,
397419
onExpandClick = { isExpanded = !isExpanded },
420+
applyIconBackground = true,
398421
)
399422
}
400423
}

app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/util/CipherViewExtensions.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ fun CipherView.toViewState(
160160
?.isVisible == true,
161161
)
162162
},
163+
paymentCardBrandIconData = card?.paymentCardBrandIconRes?.let {
164+
IconData.Local(iconRes = it)
165+
},
163166
)
164167
}
165168

@@ -252,6 +255,12 @@ private fun CipherView.toIconData(
252255
)
253256
}
254257

258+
CipherType.CARD -> {
259+
card?.paymentCardBrandIconRes
260+
?.let { IconData.Local(iconRes = it) }
261+
?: IconData.Local(type.iconRes)
262+
}
263+
255264
else -> {
256265
IconData.Local(iconRes = this.type.iconRes)
257266
}
@@ -268,6 +277,24 @@ private val CipherType.iconRes: Int
268277
CipherType.LOGIN -> R.drawable.ic_globe
269278
}
270279

280+
@get:DrawableRes
281+
private val CardView.paymentCardBrandIconRes: Int?
282+
get() = when (this.cardBrand) {
283+
VaultCardBrand.VISA -> R.drawable.ic_payment_card_brand_visa
284+
VaultCardBrand.MASTERCARD -> R.drawable.ic_payment_card_brand_mastercard
285+
VaultCardBrand.AMEX -> R.drawable.ic_payment_card_brand_amex
286+
VaultCardBrand.DISCOVER -> R.drawable.ic_payment_card_brand_discover
287+
VaultCardBrand.DINERS_CLUB -> R.drawable.ic_payment_card_brand_diners_club
288+
VaultCardBrand.JCB -> R.drawable.ic_payment_card_brand_jcb
289+
VaultCardBrand.MAESTRO -> R.drawable.ic_payment_card_brand_maestro
290+
VaultCardBrand.UNIONPAY -> R.drawable.ic_payment_card_brand_union_pay
291+
VaultCardBrand.RUPAY -> R.drawable.ic_payment_card_brand_ru_pay
292+
VaultCardBrand.SELECT,
293+
VaultCardBrand.OTHER,
294+
null,
295+
-> null
296+
}
297+
271298
private val IdentityView.identityAddress: String?
272299
get() = listOfNotNull(
273300
address1,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="19dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="19">
6+
<path
7+
android:pathData="M7.2,2.41h16.8v15.664h-16.8z"
8+
android:fillColor="#ffffff"
9+
android:fillAlpha="0.87"/>
10+
<path
11+
android:pathData="M21.558,16.459L20.315,15.081L19.024,16.459H16.491H11.041V10.046H8.508L11.663,2.874H14.722L15.822,5.344V2.874H19.91L20.315,4.726L20.984,2.874L23.982,2.874L24,2.375C24,1.188 23.4,0 21.558,0H2.4C1.2,0 0,1.188 0,2.375V16.625C0,17.813 1.2,19 2.4,19H21.6C23.4,19 24,17.884 24,17.219L23.982,16.459L21.558,16.459Z"
12+
android:fillColor="#0071CE"/>
13+
<path
14+
android:pathData="M21.991,15.617H23.999L21.37,12.785L23.999,10.001H22.039L20.366,11.825L18.741,10.001H16.733L19.41,12.833L16.733,15.617H18.693L20.366,13.793L21.991,15.617Z"
15+
android:fillColor="#0071CE"/>
16+
<path
17+
android:pathData="M13.531,14.321V13.457H16.686V12.161H13.531V11.297H16.734V10.001H12.002V15.617H16.734V14.321H13.531Z"
18+
android:fillColor="#0071CE"/>
19+
<path
20+
android:pathData="M22.517,9.185H23.951V3.521L21.657,3.521L20.414,7.073L19.123,3.569H16.733V9.185H18.263V5.249L19.697,9.185H21.035L22.517,5.249V9.185Z"
21+
android:fillColor="#0071CE"/>
22+
<path
23+
android:pathData="M14.295,3.569H12.335L9.85,9.185H11.523L12.001,8.081H14.582L15.06,9.185H16.781L14.295,3.569ZM12.527,6.785L13.292,4.961L14.056,6.785H12.527Z"
24+
android:fillColor="#0071CE"/>
25+
<path
26+
android:pathData="M22.596,12.99L23.982,14.526V11.454L22.596,12.99Z"
27+
android:fillColor="#0071CE"/>
28+
</vector>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="19dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="19">
6+
<path
7+
android:pathData="M0,2C0,0.895 0.895,0 2,0H22C23.105,0 24,0.895 24,2V17C24,18.105 23.105,19 22,19H2C0.895,19 0,18.105 0,17V2Z"
8+
android:fillColor="#2F343D"/>
9+
<path
10+
android:pathData="M13.236,15.762C16.726,15.819 19.628,13.092 19.788,9.606C19.762,7.903 19.054,6.282 17.822,5.106C16.591,3.93 14.938,3.298 13.236,3.351H10.316C8.635,3.296 7.007,3.935 5.812,5.119C4.618,6.302 3.963,7.925 4.002,9.606C4.017,11.261 4.693,12.842 5.878,13.998C7.063,15.153 8.661,15.789 10.316,15.762H13.236Z"
11+
android:fillColor="#0779BE"/>
12+
<path
13+
android:pathData="M10.331,3.864C7.193,3.864 4.648,6.408 4.648,9.547C4.648,12.685 7.193,15.23 10.331,15.23C13.47,15.23 16.014,12.685 16.014,9.547C16.014,6.408 13.47,3.864 10.331,3.864ZM6.719,9.547C6.722,8.053 7.649,6.717 9.047,6.192V12.901C7.649,12.376 6.722,11.04 6.719,9.547ZM11.617,6.192V12.921C12.987,12.363 13.879,11.026 13.867,9.547C13.882,8.071 12.988,6.738 11.617,6.192Z"
14+
android:fillColor="#ffffff"
15+
android:fillType="evenOdd"/>
16+
<path
17+
android:pathData="M10.331,3.864C7.193,3.864 4.648,6.408 4.648,9.547C4.648,12.685 7.193,15.23 10.331,15.23C13.47,15.23 16.014,12.685 16.014,9.547C16.014,6.408 13.47,3.864 10.331,3.864ZM6.719,9.547C6.722,8.053 7.649,6.717 9.047,6.192V12.901C7.649,12.376 6.722,11.04 6.719,9.547ZM11.617,6.192V12.921C12.987,12.363 13.879,11.026 13.867,9.547C13.882,8.071 12.988,6.738 11.617,6.192Z"
18+
android:fillColor="#ffffff"
19+
android:fillType="evenOdd"/>
20+
</vector>

0 commit comments

Comments
 (0)