commit fc569a3a0b1831cb1174a5ffdde497786a94c21c
parent a4876ab450ebfe3cf334116f52af03b00195ccb5
Author: Matthew Tighe <matthewdtighe@gmail.com>
Date: Tue, 30 Sep 2025 21:36:58 +0000
Bug 1991169 - add credit cards debug drawer page r=android-reviewers,android-l10n-reviewers,sfamisa,007,delphine
Differential Revision: https://phabricator.services.mozilla.com/D266438
Diffstat:
6 files changed, 203 insertions(+), 5 deletions(-)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/addresses/FakeCreditCardsAddressesStorage.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/addresses/FakeCreditCardsAddressesStorage.kt
@@ -7,6 +7,7 @@ package org.mozilla.fenix.debugsettings.addresses
import mozilla.components.concept.storage.Address
import mozilla.components.concept.storage.CreditCard
import mozilla.components.concept.storage.CreditCardCrypto
+import mozilla.components.concept.storage.CreditCardNumber
import mozilla.components.concept.storage.CreditCardsAddressesStorage
import mozilla.components.concept.storage.NewCreditCardFields
import mozilla.components.concept.storage.UpdatableAddressFields
@@ -84,8 +85,12 @@ internal class FakeCreditCardsAddressesStorage : CreditCardsAddressesStorage {
it.generateFakeAddressForLangTag().toAddress()
}.toMutableList()
+ val creditCards = mutableListOf<CreditCard>()
+
override suspend fun addCreditCard(creditCardFields: NewCreditCardFields): CreditCard {
- throw UnsupportedOperationException()
+ return creditCardFields.toCreditCard().also {
+ creditCards.add(it)
+ }
}
override suspend fun updateCreditCard(
@@ -99,12 +104,10 @@ internal class FakeCreditCardsAddressesStorage : CreditCardsAddressesStorage {
throw UnsupportedOperationException()
}
- override suspend fun getAllCreditCards(): List<CreditCard> {
- throw UnsupportedOperationException()
- }
+ override suspend fun getAllCreditCards(): List<CreditCard> = creditCards
override suspend fun deleteCreditCard(guid: String): Boolean {
- throw UnsupportedOperationException()
+ return creditCards.remove(creditCards.find { it.guid == guid })
}
override suspend fun touchCreditCard(guid: String) {
@@ -162,11 +165,41 @@ internal class FakeCreditCardsAddressesStorage : CreditCardsAddressesStorage {
email = email,
)
+ private fun NewCreditCardFields.toCreditCard() = CreditCard(
+ guid = UUID.randomUUID().toString(),
+ billingName = billingName,
+ cardNumberLast4 = cardNumberLast4,
+ expiryMonth = expiryMonth,
+ expiryYear = expiryYear,
+ cardType = cardType,
+ encryptedCardNumber = CreditCardNumber.Encrypted(plaintextCardNumber.number),
+ )
+
companion object {
fun getAllPossibleLocaleLangTags(): List<String> = listOf(
"en-US",
"en-CA",
"fr-CA",
) + DebugLocale.entries.map { it.langTag }
+
+ private val randomNames = listOf("John Doe", "Jane Doe", "Bob Smith", "Alice Johnson")
+ private val randomCardNumbers = listOf(
+ "1111 2222 3333 4444",
+ "5555 6666 7777 8888",
+ "9999 0000 1111 2222",
+ )
+ private val randomCardTypes = listOf("Visa", "Mastercard", "American Express")
+
+ fun generateCreditCard(): NewCreditCardFields = randomCardNumbers.random().run {
+ val last4DigitsOfCard = 4
+ NewCreditCardFields(
+ billingName = randomNames.random(),
+ plaintextCardNumber = CreditCardNumber.Plaintext(this),
+ cardNumberLast4 = this.takeLast(last4DigitsOfCard),
+ expiryMonth = (1L..12L).random(),
+ expiryYear = (2026L..2032L).random(),
+ cardType = randomCardTypes.random(),
+ )
+ }
}
}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/creditcards/CreditCardsTools.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/creditcards/CreditCardsTools.kt
@@ -0,0 +1,133 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.fenix.debugsettings.creditcards
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.PreviewLightDark
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.launch
+import mozilla.components.compose.base.button.FilledButton
+import mozilla.components.concept.storage.CreditCard
+import mozilla.components.concept.storage.CreditCardsAddressesStorage
+import org.mozilla.fenix.R
+import org.mozilla.fenix.compose.list.TextListItem
+import org.mozilla.fenix.debugsettings.addresses.FakeCreditCardsAddressesStorage
+import org.mozilla.fenix.theme.FirefoxTheme
+
+/**
+ * CreditCards UI for the debug drawer that displays various creditCards related tools.
+ */
+@Composable
+fun CreditCardsTools(
+ creditCardsAddressesStorage: CreditCardsAddressesStorage,
+) {
+ val scope = rememberCoroutineScope()
+ var creditCards by remember { mutableStateOf(listOf<CreditCard>()) }
+ LaunchedEffect(Unit) {
+ creditCards = creditCardsAddressesStorage.getAllCreditCards()
+ }
+ val onAddCreditCard: () -> Unit = {
+ scope.launch {
+ creditCardsAddressesStorage.addCreditCard(FakeCreditCardsAddressesStorage.generateCreditCard())
+ creditCards = creditCardsAddressesStorage.getAllCreditCards()
+ }
+ }
+ val onDeleteCreditCard: (CreditCard) -> Unit = { creditCard ->
+ scope.launch {
+ creditCardsAddressesStorage.deleteCreditCard(creditCard.guid)
+ creditCards = creditCardsAddressesStorage.getAllCreditCards()
+ }
+ }
+ val onDeleteAllCreditCards: () -> Unit = {
+ scope.launch {
+ creditCardsAddressesStorage.getAllCreditCards().forEach { creditCard ->
+ creditCardsAddressesStorage.deleteCreditCard(creditCard.guid)
+ }
+ creditCards = creditCardsAddressesStorage.getAllCreditCards()
+ }
+ }
+
+ CreditCardsContent(
+ creditCards = creditCards,
+ onAddCreditCardClick = onAddCreditCard,
+ onDeleteCreditCardClick = onDeleteCreditCard,
+ onDeleteAllCreditCardsClick = onDeleteAllCreditCards,
+ )
+}
+
+@Composable
+private fun CreditCardsContent(
+ creditCards: List<CreditCard>,
+ onAddCreditCardClick: () -> Unit,
+ onDeleteCreditCardClick: (CreditCard) -> Unit,
+ onDeleteAllCreditCardsClick: () -> Unit,
+) {
+ Column {
+ Text(
+ text = stringResource(R.string.debug_drawer_credit_cards_title),
+ color = FirefoxTheme.colors.textSecondary,
+ style = FirefoxTheme.typography.headline7,
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ FilledButton(
+ text = stringResource(R.string.debug_drawer_add_new_credit_card),
+ modifier = Modifier.fillMaxWidth(),
+ onClick = { onAddCreditCardClick() },
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ FilledButton(
+ text = stringResource(R.string.debug_drawer_delete_all_credit_cards),
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onDeleteAllCreditCardsClick,
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ Column {
+ creditCards.forEach { creditCard ->
+ TextListItem(
+ label = creditCard.cardNumberLast4,
+ description = creditCard.billingName,
+ iconPainter = painterResource(R.drawable.ic_delete),
+ onIconClick = { onDeleteCreditCardClick(creditCard) },
+ )
+ }
+ }
+ }
+}
+
+@Composable
+@PreviewLightDark
+private fun CreditCardsScreenPreview() {
+ FirefoxTheme {
+ Box(
+ modifier = Modifier.background(color = FirefoxTheme.colors.layer1),
+ ) {
+ CreditCardsTools(
+ creditCardsAddressesStorage = FakeCreditCardsAddressesStorage(),
+ )
+ }
+ }
+}
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/navigation/DebugDrawerRoute.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/navigation/DebugDrawerRoute.kt
@@ -17,6 +17,7 @@ import org.mozilla.fenix.debugsettings.addresses.AddressesTools
import org.mozilla.fenix.debugsettings.cfrs.CfrToolsState
import org.mozilla.fenix.debugsettings.cfrs.CfrToolsStore
import org.mozilla.fenix.debugsettings.crashtools.CrashTools
+import org.mozilla.fenix.debugsettings.creditcards.CreditCardsTools
import org.mozilla.fenix.debugsettings.gleandebugtools.GleanDebugToolsStore
import org.mozilla.fenix.debugsettings.gleandebugtools.ui.GleanDebugToolsScreen
import org.mozilla.fenix.debugsettings.logins.LoginsTools
@@ -49,6 +50,10 @@ enum class DebugDrawerRoute(val route: String, @param:StringRes val title: Int)
route = "addresses",
title = R.string.debug_drawer_addresses_title,
),
+ CreditCards(
+ route = "credit_cards",
+ title = R.string.debug_drawer_credit_cards_title,
+ ),
CfrTools(
route = "cfr_tools",
title = R.string.debug_drawer_cfr_tools_title,
@@ -135,6 +140,17 @@ enum class DebugDrawerRoute(val route: String, @param:StringRes val title: Int)
}
}
+ CreditCards -> {
+ onClick = {
+ debugDrawerStore.dispatch(DebugDrawerAction.NavigateTo.CreditCards)
+ }
+ content = {
+ CreditCardsTools(
+ creditCardsAddressesStorage = creditCardsAddressesStorage,
+ )
+ }
+ }
+
CfrTools -> {
onClick = {
debugDrawerStore.dispatch(DebugDrawerAction.NavigateTo.CfrTools)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/store/DebugDrawerAction.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/store/DebugDrawerAction.kt
@@ -54,6 +54,11 @@ sealed class DebugDrawerAction : Action {
data object Addresses : NavigateTo()
/**
+ * [NavigateTo] action fired when the debug drawer needs to navigate to [CreditCardsTools].
+ */
+ data object CreditCards : NavigateTo()
+
+ /**
* [NavigateTo] action fired when the debug drawer needs to navigate to [CfrToolsScreen].
*/
object CfrTools : NavigateTo()
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/store/DebugDrawerNavigationMiddleware.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/debugsettings/store/DebugDrawerNavigationMiddleware.kt
@@ -41,6 +41,8 @@ class DebugDrawerNavigationMiddleware(
navController.navigate(route = DebugDrawerRoute.Logins.route)
is DebugDrawerAction.NavigateTo.Addresses ->
navController.navigate(route = DebugDrawerRoute.Addresses.route)
+ is DebugDrawerAction.NavigateTo.CreditCards ->
+ navController.navigate(route = DebugDrawerRoute.CreditCards.route)
is DebugDrawerAction.NavigateTo.CfrTools ->
navController.navigate(route = DebugDrawerRoute.CfrTools.route)
is DebugDrawerAction.NavigateTo.GleanDebugTools ->
diff --git a/mobile/android/fenix/app/src/main/res/values/strings.xml b/mobile/android/fenix/app/src/main/res/values/strings.xml
@@ -3297,6 +3297,15 @@
<!-- The title of the button for deleting in the debug drawer all addresses. -->
<string name="debug_drawer_delete_all_addresses">Delete all addresses</string>
+ <!-- Debug drawer credit cards -->
+ <!-- The title of the credit cards feature in the Debug Drawer. -->
+ <string name="debug_drawer_credit_cards_title">Credit cards</string>
+ <!-- The title of the button in the debug drawer for adding a new credit card -->
+ <string name="debug_drawer_add_new_credit_card">Add new credit card</string>
+ <!-- The title of the button for deleting in the debug drawer all addresses. -->
+ <string name="debug_drawer_delete_all_credit_cards">Delete all credit cards</string>
+
+
<!-- Debug drawer "contextual feature recommendation" (CFR) tools -->
<!-- The title of the CFR Tools feature in the Debug Drawer -->
<string name="debug_drawer_cfr_tools_title">CFR Tools</string>