diff options
author | Iván Ávalos <avalos@disroot.org> | 2024-02-20 14:11:56 -0600 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2024-03-27 14:26:43 -0300 |
commit | 390b562828b8ef0da98c2c8cf06a88055b2c8695 (patch) | |
tree | 14f7462d740ef14f2f86c31328c17e01ae4bef20 | |
parent | e305ddba1455a33e8dec037d4fcef8498e0b21a5 (diff) | |
download | taler-android-390b562828b8ef0da98c2c8cf06a88055b2c8695.tar.gz taler-android-390b562828b8ef0da98c2c8cf06a88055b2c8695.tar.bz2 taler-android-390b562828b8ef0da98c2c8cf06a88055b2c8695.zip |
[wallet] Improve DD51 unit rendering and adapt tests accordingly
(cherry picked from commit c7b2cbe5c39192648336746a719ecdfa88221b28)
3 files changed, 74 insertions, 28 deletions
diff --git a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt index 4f624ae..45bc3af 100644 --- a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt +++ b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt @@ -23,8 +23,10 @@ data class SignedAmount( val amount: Amount ) { - override fun toString(): String { - return if (positive) "$amount" else "-$amount" - } + override fun toString() = toString(showSymbol = true) + fun toString(showSymbol: Boolean) = amount.toString( + showSymbol = showSymbol, + negative = !positive, + ) } diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt index d6188cf..1652056 100644 --- a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt +++ b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt @@ -23,6 +23,7 @@ import kotlinx.serialization.Serializer import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import java.text.DecimalFormat +import java.text.DecimalFormatSymbols import java.text.NumberFormat import kotlin.math.floor import kotlin.math.pow @@ -214,32 +215,39 @@ public data class Amount( fun toString( showSymbol: Boolean = true, negative: Boolean = false, + symbols: DecimalFormatSymbols = DecimalFormat().decimalFormatSymbols, ): String { - val symbols = DecimalFormat().decimalFormatSymbols - - val format = if (showSymbol) { - NumberFormat.getCurrencyInstance() - } else { - // Make sure monetary separators are the ones used! - symbols.decimalSeparator = symbols.monetaryDecimalSeparator - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - symbols.groupingSeparator = symbols.monetaryGroupingSeparator + // We clone the object to safely/cleanly modify it + val s = symbols.clone() as DecimalFormatSymbols + val amount = (if (negative) "-$amountStr" else amountStr).toBigDecimal() + + // No currency spec, so we render normally + if (spec == null) { + val format = NumberFormat.getInstance() + format.maximumFractionDigits = MAX_FRACTION_LENGTH + format.minimumFractionDigits = 0 + if (Build.VERSION.SDK_INT >= 34) { + s.groupingSeparator = s.monetaryGroupingSeparator } + s.decimalSeparator = s.monetaryDecimalSeparator + (format as DecimalFormat).decimalFormatSymbols = s - NumberFormat.getInstance() + val fmt = format.format(amount) + return if (showSymbol) "$fmt $currency" else fmt } - if (spec != null) { - symbols.currencySymbol = spec.symbol(this) - format.maximumFractionDigits = spec.numFractionalNormalDigits - format.minimumFractionDigits = spec.numFractionalTrailingZeroDigits - } else { - symbols.currencySymbol = currency + // There is currency spec, so we can do things right + val format = NumberFormat.getCurrencyInstance() + format.maximumFractionDigits = spec.numFractionalNormalDigits + format.minimumFractionDigits = spec.numFractionalTrailingZeroDigits + s.currencySymbol = spec.symbol(this) + (format as DecimalFormat).decimalFormatSymbols = s + + val fmt = format.format(amount) + return if (showSymbol) fmt else { + // We should do better than manually removing the symbol here + fmt.replace(s.currencySymbol, "").trim() } - - (format as DecimalFormat).decimalFormatSymbols = symbols - - return format.format((if (negative) "-$amountStr" else amountStr).toBigDecimal()) } override fun compareTo(other: Amount): Int { diff --git a/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt b/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt index 7072426..e7fb273 100644 --- a/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt +++ b/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt @@ -16,10 +16,12 @@ package net.taler.common +import android.os.Build import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test +import java.text.DecimalFormatSymbols import kotlin.random.Random class AmountTest { @@ -41,7 +43,6 @@ class AmountTest { assertEquals("TESTKUDOS", amount.currency) assertEquals(23, amount.value) assertEquals((0.42 * 1e8).toInt(), amount.fraction) - assertEquals("23.42 TESTKUDOS", amount.toString()) str = "EUR:500000000.00000001" amount = Amount.fromJSONString(str) @@ -49,7 +50,6 @@ class AmountTest { assertEquals("EUR", amount.currency) assertEquals(500000000, amount.value) assertEquals(1, amount.fraction) - assertEquals("500000000.00000001 EUR", amount.toString()) str = "EUR:1500000000.00000003" amount = Amount.fromJSONString(str) @@ -57,14 +57,51 @@ class AmountTest { assertEquals("EUR", amount.currency) assertEquals(1500000000, amount.value) assertEquals(3, amount.fraction) - assertEquals("1500000000.00000003 EUR", amount.toString()) } @Test fun testToString() { + val symbols = DecimalFormatSymbols.getInstance() + symbols.decimalSeparator = '.' + symbols.groupingSeparator = ',' + symbols.monetaryDecimalSeparator = '.' + if (Build.VERSION.SDK_INT >= 34) { + symbols.monetaryGroupingSeparator = ',' + } + + val spec = CurrencySpecification( + name = "Bitcoin", + numFractionalInputDigits = 8, + numFractionalNormalDigits = 8, + numFractionalTrailingZeroDigits = 0, + altUnitNames = mapOf( + 0 to "₿", + // TODO: uncomment when units get implemented + // and then write tests for units, please +// -1 to "d₿", +// -2 to "c₿", +// -3 to "m₿", +// -6 to "µ₿", +// -8 to "sat", + ), + ) + Amount.fromString("BITCOINBTC", "0.00000001").let { amount -> - assertEquals("0.00000001 BITCOINBTC", amount.toString()) + // Only the raw amount assertEquals("0.00000001", amount.amountStr) + + // The amount without currency spec + assertEquals("0.00000001 BITCOINBTC", amount.toString(symbols = symbols)) + assertEquals("0.00000001", amount.toString(symbols = symbols, showSymbol = false)) + assertEquals("-0.00000001 BITCOINBTC", amount.toString(symbols = symbols, negative = true)) + assertEquals("-0.00000001", amount.toString(symbols = symbols, showSymbol = false, negative = true)) + + // The amount with currency spec + val withSpec = amount.withSpec(spec) + assertEquals("₿0.00000001", withSpec.toString(symbols = symbols)) + assertEquals("0.00000001", withSpec.toString(symbols = symbols, showSymbol = false)) + assertEquals("-₿0.00000001", withSpec.toString(symbols = symbols, negative = true)) + assertEquals("-0.00000001", withSpec.toString(symbols = symbols, showSymbol = false, negative = true)) } } @@ -76,7 +113,6 @@ class AmountTest { assertEquals(str, amount.toJSONString()) assertEquals("TESTKUDOS123", amount.currency) assertEquals(maxValue, amount.value) - assertEquals("$maxValue.99999999 TESTKUDOS123", amount.toString()) // longer currency not accepted assertThrows<AmountParserException>("longer currency was accepted") { |