From fc91ca70fd7b7a6bcab88f3bd4bd839abfaca011 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 5 Nov 2024 19:27:46 +0100 Subject: [PATCH 1/3] Add the ability to copy payment method data to clipboard COAND-1005 --- .../java/com/adyen/testcards/ui/CopyMenu.kt | 56 +++++++++++++++++++ .../java/com/adyen/testcards/ui/CreditCard.kt | 20 ++++++- .../java/com/adyen/testcards/ui/GiftCard.kt | 19 ++++++- .../main/java/com/adyen/testcards/ui/IBAN.kt | 18 +++++- .../main/java/com/adyen/testcards/ui/UPI.kt | 17 +++++- .../adyen/testcards/ui/UsernamePassword.kt | 19 ++++++- app/src/main/res/drawable/ic_copy.xml | 5 ++ 7 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt create mode 100644 app/src/main/res/drawable/ic_copy.xml diff --git a/app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt b/app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt new file mode 100644 index 0000000..4ebac8a --- /dev/null +++ b/app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt @@ -0,0 +1,56 @@ +package com.adyen.testcards.ui + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.ClipboardManager +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.unit.dp +import com.adyen.testcards.R + +@Composable +fun CopyMenu( + expanded: Boolean, + items: Map, + onDismissRequest: () -> Unit, +) { + DropdownMenu( + expanded = expanded, + onDismissRequest = onDismissRequest, + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + ) { + Icon(ImageVector.vectorResource(R.drawable.ic_copy), null) + Spacer(Modifier.width(4.dp)) + Text( + text = "Copy...", + style = MaterialTheme.typography.bodyMedium, + ) + } + + val clipboardManager: ClipboardManager = LocalClipboardManager.current + items.forEach { (title, value) -> + DropdownMenuItem( + text = { Text(title) }, + onClick = { + clipboardManager.setText(AnnotatedString(value)) + onDismissRequest() + }, + ) + } + } +} diff --git a/app/src/main/java/com/adyen/testcards/ui/CreditCard.kt b/app/src/main/java/com/adyen/testcards/ui/CreditCard.kt index 96e2144..4c1444f 100644 --- a/app/src/main/java/com/adyen/testcards/ui/CreditCard.kt +++ b/app/src/main/java/com/adyen/testcards/ui/CreditCard.kt @@ -1,6 +1,7 @@ package com.adyen.testcards.ui -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -68,6 +69,7 @@ internal fun LazyListScope.creditCardSection( } } +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun CreditCard( card: CreditCard, @@ -76,6 +78,7 @@ internal fun CreditCard( onClick: ((CreditCard) -> Unit)? = null, ) { var isFavorite by remember(card) { mutableStateOf(card.isFavorite) } + var showCopyMenu by remember(card) { mutableStateOf(false) } FavoritableRow( isFavorite = isFavorite, onFavoriteClicked = { @@ -84,7 +87,10 @@ internal fun CreditCard( }, icon = card.icon, modifier = modifier - .clickable(enabled = onClick != null) { onClick?.invoke(card) } + .combinedClickable( + onClick = { onClick?.invoke(card) }, + onLongClick = { showCopyMenu = true }, + ) .fillMaxWidth() .padding(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp), ) { @@ -109,6 +115,16 @@ internal fun CreditCard( } } } + + CopyMenu( + expanded = showCopyMenu, + onDismissRequest = { showCopyMenu = false }, + items = buildMap { + set("Number", card.number) + set("Expiry date", card.expiryDate) + card.securityCode?.let { set("Security code", it) } + }, + ) } } diff --git a/app/src/main/java/com/adyen/testcards/ui/GiftCard.kt b/app/src/main/java/com/adyen/testcards/ui/GiftCard.kt index bad877e..422b367 100644 --- a/app/src/main/java/com/adyen/testcards/ui/GiftCard.kt +++ b/app/src/main/java/com/adyen/testcards/ui/GiftCard.kt @@ -1,6 +1,7 @@ package com.adyen.testcards.ui -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -48,6 +49,7 @@ internal fun LazyListScope.giftCardSection( } } +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun GiftCard( giftCard: GiftCard, @@ -56,6 +58,7 @@ internal fun GiftCard( onClick: ((GiftCard) -> Unit)? = null, ) { var isFavorite by remember(giftCard) { mutableStateOf(giftCard.isFavorite) } + var showCopyMenu by remember(giftCard) { mutableStateOf(false) } FavoritableRow( isFavorite = isFavorite, onFavoriteClicked = { @@ -64,7 +67,10 @@ internal fun GiftCard( }, icon = R.drawable.ic_pm_gift_card.takeIf { giftCard.showIcon }, modifier = modifier - .clickable(enabled = onClick != null) { onClick?.invoke(giftCard) } + .combinedClickable( + onClick = { onClick?.invoke(giftCard) }, + onLongClick = { showCopyMenu = true }, + ) .fillMaxWidth() .padding(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp), ) { @@ -76,6 +82,15 @@ internal fun GiftCard( Text(text = giftCard.securityCode, style = MaterialTheme.typography.labelSmall) } } + + CopyMenu( + expanded = showCopyMenu, + onDismissRequest = { showCopyMenu = false }, + items = buildMap { + set("Number", giftCard.number) + set("Security code", giftCard.securityCode) + }, + ) } } diff --git a/app/src/main/java/com/adyen/testcards/ui/IBAN.kt b/app/src/main/java/com/adyen/testcards/ui/IBAN.kt index 7d03784..15aa02d 100644 --- a/app/src/main/java/com/adyen/testcards/ui/IBAN.kt +++ b/app/src/main/java/com/adyen/testcards/ui/IBAN.kt @@ -1,6 +1,7 @@ package com.adyen.testcards.ui -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -48,6 +49,7 @@ internal fun LazyListScope.ibanSection( } } +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun IBAN( iban: IBAN, @@ -56,6 +58,7 @@ internal fun IBAN( onClick: ((IBAN) -> Unit)? = null, ) { var isFavorite by remember(iban) { mutableStateOf(iban.isFavorite) } + var showCopyMenu by remember(iban) { mutableStateOf(false) } FavoritableRow( isFavorite = isFavorite, onFavoriteClicked = { @@ -64,7 +67,10 @@ internal fun IBAN( }, icon = R.drawable.ic_pm_bank.takeIf { iban.showIcon }, modifier = modifier - .clickable(enabled = onClick != null) { onClick?.invoke(iban) } + .combinedClickable( + onClick = { onClick?.invoke(iban) }, + onLongClick = { showCopyMenu = true }, + ) .fillMaxWidth() .padding(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp), ) { @@ -76,6 +82,14 @@ internal fun IBAN( Text(text = iban.issuingCountry, style = MaterialTheme.typography.labelSmall) } } + + CopyMenu( + expanded = showCopyMenu, + onDismissRequest = { showCopyMenu = false }, + items = buildMap { + set("IBAN", iban.iban) + }, + ) } } diff --git a/app/src/main/java/com/adyen/testcards/ui/UPI.kt b/app/src/main/java/com/adyen/testcards/ui/UPI.kt index 7c67b06..f26a314 100644 --- a/app/src/main/java/com/adyen/testcards/ui/UPI.kt +++ b/app/src/main/java/com/adyen/testcards/ui/UPI.kt @@ -1,6 +1,8 @@ package com.adyen.testcards.ui +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope @@ -42,6 +44,7 @@ internal fun LazyListScope.upiSection( } } +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun UPI( upi: UPI, @@ -50,6 +53,7 @@ internal fun UPI( onClick: ((UPI) -> Unit)? = null, ) { var isFavorite by remember(upi) { mutableStateOf(upi.isFavorite) } + var showCopyMenu by remember(upi) { mutableStateOf(false) } FavoritableRow( isFavorite = isFavorite, onFavoriteClicked = { @@ -58,11 +62,22 @@ internal fun UPI( }, icon = R.drawable.ic_pm_upi.takeIf { upi.showIcon }, modifier = modifier - .clickable(enabled = onClick != null) { onClick?.invoke(upi) } + .combinedClickable( + onClick = { onClick?.invoke(upi) }, + onLongClick = { showCopyMenu = true }, + ) .fillMaxWidth() .padding(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp), ) { Text(text = upi.virtualPaymentAddress) + + CopyMenu( + expanded = showCopyMenu, + onDismissRequest = { showCopyMenu = false }, + items = buildMap { + set("Virtual Payment Address", upi.virtualPaymentAddress) + }, + ) } } diff --git a/app/src/main/java/com/adyen/testcards/ui/UsernamePassword.kt b/app/src/main/java/com/adyen/testcards/ui/UsernamePassword.kt index fa2281b..4c54503 100644 --- a/app/src/main/java/com/adyen/testcards/ui/UsernamePassword.kt +++ b/app/src/main/java/com/adyen/testcards/ui/UsernamePassword.kt @@ -1,6 +1,7 @@ package com.adyen.testcards.ui -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -48,6 +49,7 @@ internal fun LazyListScope.usernamePasswordSection( } } +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun UsernamePassword( data: UsernamePassword, @@ -56,6 +58,7 @@ internal fun UsernamePassword( onClick: ((UsernamePassword) -> Unit)? = null, ) { var isFavorite by remember(data) { mutableStateOf(data.isFavorite) } + var showCopyMenu by remember(data) { mutableStateOf(false) } FavoritableRow( isFavorite = isFavorite, onFavoriteClicked = { @@ -64,7 +67,10 @@ internal fun UsernamePassword( }, icon = R.drawable.ic_pm_wallet.takeIf { data.showIcon }, modifier = modifier - .clickable(enabled = onClick != null) { onClick?.invoke(data) } + .combinedClickable( + onClick = { onClick?.invoke(data) }, + onLongClick = { showCopyMenu = true }, + ) .fillMaxWidth() .padding(start = 16.dp, top = 8.dp, end = 8.dp, bottom = 8.dp), ) { @@ -76,6 +82,15 @@ internal fun UsernamePassword( Text(text = data.password, style = MaterialTheme.typography.labelSmall) } } + + CopyMenu( + expanded = showCopyMenu, + onDismissRequest = { showCopyMenu = false }, + items = buildMap { + set("Username", data.username) + set("Password", data.password) + }, + ) } } diff --git a/app/src/main/res/drawable/ic_copy.xml b/app/src/main/res/drawable/ic_copy.xml new file mode 100644 index 0000000..7a5c08a --- /dev/null +++ b/app/src/main/res/drawable/ic_copy.xml @@ -0,0 +1,5 @@ + + + + + From 399d19d365124283fd17b4639480f0fcb151f256 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 5 Nov 2024 19:27:56 +0100 Subject: [PATCH 2/3] Update versions COAND-1005 --- app/src/main/java/com/adyen/testcards/ui/UPI.kt | 1 - gradle/libs.versions.toml | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/adyen/testcards/ui/UPI.kt b/app/src/main/java/com/adyen/testcards/ui/UPI.kt index f26a314..e35b15e 100644 --- a/app/src/main/java/com/adyen/testcards/ui/UPI.kt +++ b/app/src/main/java/com/adyen/testcards/ui/UPI.kt @@ -1,7 +1,6 @@ package com.adyen.testcards.ui import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3843456..71cb808 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,17 +1,17 @@ [versions] -activityCompose = "1.9.2" +activityCompose = "1.9.3" agp = "8.6.1" appcompat = "1.7.0" autofill = "1.3.0-beta01" -composeBom = "2024.09.02" -coreKtx = "1.13.1" +composeBom = "2024.10.01" +coreKtx = "1.15.0" datastore = "1.1.1" espressoCore = "3.6.1" hilt = "2.52" junit = "4.13.2" junitVersion = "1.2.1" -kotlin = "2.0.20" -ksp = "2.0.20-1.0.25" +kotlin = "2.0.21" +ksp = "2.0.21-1.0.25" material = "1.12.0" moshi = "1.15.1" protobuf = "4.28.1" From 77ee3737b0ff78511fe9acb46cb85677345bba1b Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Thu, 7 Nov 2024 09:28:41 +0100 Subject: [PATCH 3/3] Change styling of title in copy menu COAND-1030 --- .../java/com/adyen/testcards/ui/CopyMenu.kt | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt b/app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt index 4ebac8a..fe4a097 100644 --- a/app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt +++ b/app/src/main/java/com/adyen/testcards/ui/CopyMenu.kt @@ -1,24 +1,17 @@ package com.adyen.testcards.ui -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.Icon +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp -import com.adyen.testcards.R @Composable fun CopyMenu( @@ -30,17 +23,14 @@ fun CopyMenu( expanded = expanded, onDismissRequest = onDismissRequest, ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), - ) { - Icon(ImageVector.vectorResource(R.drawable.ic_copy), null) - Spacer(Modifier.width(4.dp)) - Text( - text = "Copy...", - style = MaterialTheme.typography.bodyMedium, - ) - } + Text( + text = "Copy...", + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f), + modifier = Modifier.padding(start = 12.dp, top = 8.dp, end = 12.dp, bottom = 16.dp), + ) + + HorizontalDivider() val clipboardManager: ClipboardManager = LocalClipboardManager.current items.forEach { (title, value) ->