fb79f4f138ac
fix: Match browser card add-entry fields (v2.3.14)
- time
- 2026-06-26 01:19 UTC · 4 months ago
- machine
- koh2
- parent
- 610b588ac078
↓ peach-android-fb79f4f1.tar.gzatlas at this save
files changed8 files
app/build.gradle+2 −2app/src/main/java/com/peachpasswords/ui/vault/CardDetailFields.kt+5 −2app/src/main/java/com/peachpasswords/ui/vault/CardEditFields.kt+9 −0app/src/main/java/com/peachpasswords/ui/vault/CardFieldUtils.kt+6 −5app/src/main/java/com/peachpasswords/ui/vault/EntryDetailViewModel.kt+2 −1app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+2 −0app/src/main/java/com/peachpasswords/ui/vault/EntryEditViewModel.kt+5 −0app/src/test/java/com/peachpasswords/ui/vault/EntryEditScreenTest.kt+10 −0app/build.gradle+2 −2
@@ -16,8 +16,8 @@1616 applicationId "com.peachpasswords"1717 minSdk 261818 targetSdk 3519- versionCode 23130020- versionName "2.3.13"19+ versionCode 23140020+ versionName "2.3.14"2121 2222 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"2323 vectorDrawables {app/src/main/java/com/peachpasswords/ui/vault/CardDetailFields.kt+5 −2
@@ -55,7 +55,7 @@5555 onRevealCvc: () -> Unit,5656 onMaskCvc: () -> Unit,5757 onCopy: (field: String, value: String?) -> Unit,58- onSaveCard: (cardNumber: String, cardholderName: String, expiry: String, cvc: String) -> Unit,58+ onSaveCard: (cardNickname: String, cardNumber: String, cardholderName: String, expiry: String, cvc: String) -> Unit,5959 copyButton: @Composable (copied: Boolean, onCopy: () -> Unit) -> Unit,6060 ) {6161 val accent = LocalPeachAccent.current@@ -65,6 +65,7 @@6565 6666 if (editing) {6767 var cardNumber by remember(entry.id, entry.modifiedAt) { mutableStateOf(CardFieldUtils.formatPan(entry.cardNumber.orEmpty(), network)) }68+ var cardNickname by remember(entry.id, entry.modifiedAt) { mutableStateOf(entry.cardNickname.orEmpty()) }6869 var cardholder by remember(entry.id, entry.modifiedAt) { mutableStateOf(entry.cardholderName.orEmpty()) }6970 var expiry by remember(entry.id, entry.modifiedAt) {7071 mutableStateOf(CardFieldUtils.displayExpiry(entry.expMonth, entry.expYear))@@ -73,12 +74,14 @@7374 var showCvc by remember { mutableStateOf(false) }7475 val draftNetwork = CardFieldUtils.detectNetwork(cardNumber)7576 CardEditFields(77+ cardNickname = cardNickname,7678 cardNumber = cardNumber,7779 cardholderName = cardholder,7880 expiry = expiry,7981 cvc = cvc,8082 showCvc = showCvc,8183 network = draftNetwork,84+ onCardNickname = { cardNickname = it },8285 onCardNumber = { cardNumber = it },8386 onCardholder = { cardholder = it },8487 onExpiry = { expiry = it },@@ -89,7 +92,7 @@8992 Text(9093 "✓ save", color = accent, style = PeachType.labelMono,9194 modifier = Modifier.clip(RoundedCornerShape(6.dp)).clickable {92- onSaveCard(cardNumber, cardholder, expiry, cvc)95+ onSaveCard(cardNickname, cardNumber, cardholder, expiry, cvc)9396 editing = false9497 }.padding(vertical = 4.dp),9598 )app/src/main/java/com/peachpasswords/ui/vault/CardEditFields.kt+9 −0
@@ -29,18 +29,27 @@2929 */3030 @Composable3131 fun CardEditFields(32+ cardNickname: String,3233 cardNumber: String,3334 cardholderName: String,3435 expiry: String,3536 cvc: String,3637 showCvc: Boolean,3738 network: String,39+ onCardNickname: (String) -> Unit,3840 onCardNumber: (String) -> Unit,3941 onCardholder: (String) -> Unit,4042 onExpiry: (String) -> Unit,4143 onCvc: (String) -> Unit,4244 onToggleShowCvc: () -> Unit,4345 ) {46+ PeachField(47+ label = "nickname",48+ value = cardNickname,49+ onChange = onCardNickname,50+ mono = false,51+ )52+4453 // CARD NUMBER — auto-formatted, network label beside the label.4554 PeachField(4655 label = "card number",app/src/main/java/com/peachpasswords/ui/vault/CardFieldUtils.kt+6 −5
@@ -14,11 +14,12 @@1414 fun detectNetwork(rawNumber: String): String {1515 val digits = rawNumber.filter { it.isDigit() }1616 if (digits.isEmpty()) return "unknown"17- return when (digits.first()) {18- '4' -> "visa"19- '5' -> "mastercard"20- '3' -> "amex"21- '6' -> "discover"17+ return when {18+ digits.startsWith("4") -> "visa"19+ digits.length >= 2 && digits.take(2).toIntOrNull() in 51..55 -> "mastercard"20+ digits.length >= 2 && digits.take(2).toIntOrNull() in 22..27 -> "mastercard"21+ digits.startsWith("34") || digits.startsWith("37") -> "amex"22+ digits.startsWith("6011") || digits.startsWith("65") -> "discover"2223 else -> "unknown"2324 }2425 }app/src/main/java/com/peachpasswords/ui/vault/EntryDetailViewModel.kt+2 −1
@@ -188,7 +188,7 @@188188 }189189 }190190 191- fun updateCard(cardNumber: String, cardholderName: String, expiry: String, cvc: String) {191+ fun updateCard(cardNickname: String, cardNumber: String, cardholderName: String, expiry: String, cvc: String) {192192 val current = entry.value ?: return193193 val digits = cardNumber.filter { it.isDigit() }194194 val expDigits = expiry.filter { it.isDigit() }@@ -197,6 +197,7 @@197197 runCatching {198198 vault.updateEntry(199199 current.copy(200+ cardNickname = cardNickname.ifBlank { null },200201 cardNumber = digits.ifBlank { null },201202 cardholderName = cardholderName.ifBlank { null },202203 expMonth = expDigits.take(2).ifBlank { null },app/src/main/java/com/peachpasswords/ui/vault/EntryEditScreen.kt+2 −0
@@ -160,12 +160,14 @@160160 }161161 if (f.type == "card") {162162 CardEditFields(163+ cardNickname = f.cardNickname,163164 cardNumber = f.cardNumber,164165 cardholderName = f.cardholderName,165166 expiry = f.expiry,166167 cvc = f.cvc,167168 showCvc = f.showCvc,168169 network = f.cardNetwork,170+ onCardNickname = vm::setCardNickname,169171 onCardNumber = vm::setCardNumber,170172 onCardholder = vm::setCardholderName,171173 onExpiry = vm::setExpiry,app/src/main/java/com/peachpasswords/ui/vault/EntryEditViewModel.kt+5 −0
@@ -28,6 +28,7 @@2828 val additionalUrls: List<String> = emptyList(),2929 val totp: String = "",3030 val notes: String = "",31+ val cardNickname: String = "",3132 val cardNumber: String = "",3233 val cardholderName: String = "",3334 val expiry: String = "", // "MM/YY"; split into expMonth/expYear on save@@ -73,6 +74,7 @@7374 additionalUrls = it.additionalUrls.orEmpty(),7475 totp = it.totp.orEmpty(),7576 notes = it.notes.orEmpty(),77+ cardNickname = it.cardNickname.orEmpty(),7678 cardNumber = CardFieldUtils.formatPan(it.cardNumber.orEmpty()),7779 cardholderName = it.cardholderName.orEmpty(),7880 expiry = CardFieldUtils.displayExpiry(it.expMonth, it.expYear),@@ -128,6 +130,7 @@128130 fun setNotes(v: String) = _form.update { it.copy(notes = v) }129131 fun revealTotp() = _form.update { it.copy(showTotp = true) }130132 fun revealNotes() = _form.update { it.copy(showNotes = true) }133+ fun setCardNickname(v: String) = _form.update { it.copy(cardNickname = v, error = null) }131134 fun setCardNumber(v: String) = _form.update {132135 val formatted = CardFieldUtils.formatPan(v)133136 it.copy(cardNumber = formatted, cardNetwork = CardFieldUtils.detectNetwork(formatted), error = null)@@ -213,6 +216,7 @@213216 .ifEmpty { null },214217 totp = f.totp.ifBlank { null },215218 notes = f.notes.ifBlank { null },219+ cardNickname = f.cardNickname.ifBlank { null },216220 cardNumber = f.cardNumber.filter { it.isDigit() }.ifBlank { null },217221 cardholderName = f.cardholderName.ifBlank { null },218222 expMonth = f.expiry.filter { it.isDigit() }.take(2).ifBlank { null },@@ -236,6 +240,7 @@236240 .ifEmpty { null },237241 totp = f.totp.ifBlank { null },238242 notes = f.notes.ifBlank { null },243+ cardNickname = f.cardNickname.ifBlank { null },239244 cardNumber = f.cardNumber.filter { it.isDigit() }.ifBlank { null },240245 cardholderName = f.cardholderName.ifBlank { null },241246 expMonth = f.expiry.filter { it.isDigit() }.take(2).ifBlank { null },app/src/test/java/com/peachpasswords/ui/vault/EntryEditScreenTest.kt+10 −0
@@ -100,6 +100,16 @@100100 assertEquals("3782 822463 10005", CardFieldUtils.formatPan("378282246310005"))101101 }102102 103+ @Test fun card_network_detection_matches_browser_add_view_badges() {104+ assertEquals("visa", CardFieldUtils.detectNetwork("4242424242424242"))105+ assertEquals("mastercard", CardFieldUtils.detectNetwork("5555555555554444"))106+ assertEquals("mastercard", CardFieldUtils.detectNetwork("2221000000000009"))107+ assertEquals("amex", CardFieldUtils.detectNetwork("378282246310005"))108+ assertEquals("discover", CardFieldUtils.detectNetwork("6011111111111117"))109+ assertEquals("discover", CardFieldUtils.detectNetwork("6500000000000002"))110+ assertEquals("unknown", CardFieldUtils.detectNetwork("6000000000000000"))111+ }112+103113 @Test fun new_card_expiry_auto_formats_mm_yy() {104114 assertEquals("12", CardFieldUtils.formatExpiry("12"))105115 assertEquals("12/28", CardFieldUtils.formatExpiry("1228"))